promptfigure 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -15
- package/adapters/claude-code/install.mjs +7 -6
- package/adapters/claude-code/promptfigure-api/SKILL.md +274 -0
- package/adapters/claude-code/promptfigure-api/references/api-contract.md +319 -0
- package/adapters/claude-code/promptfigure-api/references/document-workflow.md +231 -0
- package/adapters/claude-code/promptfigure-api/references/figure-upgrade-workflow.md +199 -0
- package/adapters/claude-code/promptfigure-api/references/proactive-upgrade.md +120 -0
- package/adapters/claude-code/promptfigure-api/references/prompt-cookbook.md +223 -0
- package/adapters/claude-code/promptfigure-api/references/prompt-review-workflow.md +265 -0
- package/adapters/claude-code/promptfigure-api/references/setup-guide.md +151 -0
- package/adapters/claude-code/promptfigure-api/references/troubleshooting.md +204 -0
- package/adapters/codex/promptfigure/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/promptfigure/skills/promptfigure-api/SKILL.md +274 -0
- package/adapters/codex/promptfigure/skills/promptfigure-api/references/api-contract.md +319 -0
- package/adapters/codex/promptfigure/skills/promptfigure-api/references/document-workflow.md +231 -0
- package/adapters/codex/promptfigure/skills/promptfigure-api/references/figure-upgrade-workflow.md +199 -0
- package/adapters/codex/promptfigure/skills/promptfigure-api/references/proactive-upgrade.md +120 -0
- package/adapters/codex/promptfigure/skills/promptfigure-api/references/prompt-cookbook.md +223 -0
- package/adapters/codex/promptfigure/skills/promptfigure-api/references/prompt-review-workflow.md +265 -0
- package/adapters/codex/promptfigure/skills/promptfigure-api/references/setup-guide.md +151 -0
- package/adapters/codex/promptfigure/skills/promptfigure-api/references/troubleshooting.md +204 -0
- package/bin/pf.mjs +37 -0
- package/package.json +2 -2
- package/scripts/build-adapters.mjs +31 -18
- package/skill/promptfigure-api/SKILL.md +274 -0
- package/skill/promptfigure-api/references/api-contract.md +319 -0
- package/skill/promptfigure-api/references/document-workflow.md +231 -0
- package/skill/promptfigure-api/references/figure-upgrade-workflow.md +199 -0
- package/skill/promptfigure-api/references/proactive-upgrade.md +120 -0
- package/skill/promptfigure-api/references/prompt-cookbook.md +223 -0
- package/skill/promptfigure-api/references/prompt-review-workflow.md +265 -0
- package/skill/promptfigure-api/references/setup-guide.md +151 -0
- package/skill/promptfigure-api/references/troubleshooting.md +204 -0
- /package/adapters/claude-code/{SKILL.md → promptfigure-local/SKILL.md} +0 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# API 契约完整版
|
|
2
|
+
|
|
3
|
+
**站点**:https://promptfigure.top
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ⚠️ 必看
|
|
8
|
+
|
|
9
|
+
1. **默认就是完整润色管线**——`/api/v1/generate` 与网页工作台同一套(编排 + 净化 + 审查 + 出图),只是没有网页的多轮问询/二次确认。**不要因为要"一次性答完"就绕过润色。**
|
|
10
|
+
2. **`polish:false` 仅是紧急绕过**(服务端不扩写,需自己写完整英文提示词),平时**不要传**。上游文本限频期服务端会自动回落重试(主模型 agnes-2.5-flash → 备用 agnes-2.0-flash)。
|
|
11
|
+
3. **CF WAF 拦 `Python-urllib/*`**——其它客户端都过。
|
|
12
|
+
4. **`balance` 字段滞后**——`/api/login`、`/api/me` 返回的 balance 不等于真实余额。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 路径 A:API 直调(v1,同步)
|
|
17
|
+
|
|
18
|
+
**Endpoint**:`POST https://promptfigure.top/api/v1/generate`
|
|
19
|
+
**鉴权**:`Authorization: Bearer pf_...`
|
|
20
|
+
**CORS**:全开(`Access-Control-Allow-Origin: *`),鉴权靠 key 不靠 cookie。
|
|
21
|
+
|
|
22
|
+
### Request
|
|
23
|
+
|
|
24
|
+
```jsonc
|
|
25
|
+
{
|
|
26
|
+
"prompt": "", // ✅ 必填,大白话即可,≤8000 字符
|
|
27
|
+
"polish": true, // 默认润色;false 仅紧急绕过(服务端不扩写,平时不要传)
|
|
28
|
+
"model": "standard" | "premium", // 服务端默认 standard;非 "premium" 一律 standard
|
|
29
|
+
"size": "1K" | "2K", // 仅 premium 生效,默认 2K
|
|
30
|
+
"ratio": "1:1" | "3:2" | "2:3" | "16:9" | "9:16", // 默认 1:1,非法值回落 1:1
|
|
31
|
+
"refUrl": "https://.../ref.png", // 公网图片直链,PNG ≤8MB
|
|
32
|
+
"refDataUrl": "data:image/png;base64,...", // base64 后 ≤8MB
|
|
33
|
+
"mode": "replica" // 一键临摹(2026-09-18);别名 "replica":true、"mode":"一键临摹"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
规则:
|
|
38
|
+
- `prompt` 空或缺失 → `400 prompt_required`(**临摹模式例外**:`mode:"replica"` 时 `prompt` 可留空,服务端补一句中性复刻指令)
|
|
39
|
+
- `prompt` >8000 字符 → `400 prompt_too_long`
|
|
40
|
+
- `model` 非 `"premium"` 任何值 → `standard`
|
|
41
|
+
- `size` 非 `"1K"` → 2K;`standard` 恒输出 1K
|
|
42
|
+
- `refUrl` + `refDataUrl` 都传 → **`refDataUrl` 优先**
|
|
43
|
+
- `refUrl` 服务端安全限制:只接 `http(s)`;`localhost`/`.local`/`.internal`/环回与私网 IP 拒绝;校验 PNG 文件魔数(不看 content-type);≤8MB;拉取超时 20s
|
|
44
|
+
- **注意**:上游 `/images/edits` 端点 2026-09-07 起持续 503。`refUrl`/`refDataUrl` 当前可能 `refIgnored: true`(被忽略,**当次照常出图并计费**)
|
|
45
|
+
|
|
46
|
+
### Response
|
|
47
|
+
|
|
48
|
+
成功 `200`:
|
|
49
|
+
|
|
50
|
+
```jsonc
|
|
51
|
+
{
|
|
52
|
+
"b64_json": "<PNG base64>",
|
|
53
|
+
"size": "2K",
|
|
54
|
+
"ratio": "16:9",
|
|
55
|
+
"model": "premium",
|
|
56
|
+
"provider": "premium", // agnes=standard, premium=premium 档(高级档中转通道)
|
|
57
|
+
"crafted": false, // false=polish:false 直出
|
|
58
|
+
"charged": 0.15,
|
|
59
|
+
"balance": 12.34,
|
|
60
|
+
// 仅 mode:"replica" 出现:
|
|
61
|
+
"mode": "replica",
|
|
62
|
+
"referenceSpec": { /* 见下「一键临摹」 */ },
|
|
63
|
+
"specError": null // 非 null = 参考图规格提取失败,已退化(不额外计费)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
失败:
|
|
68
|
+
|
|
69
|
+
```jsonc
|
|
70
|
+
{ "error": "insufficient_balance", "required": 0.15, "balance": 0.02 } // 402
|
|
71
|
+
{ "error": "rate_limited", "limit": 5 } // 429,已退款
|
|
72
|
+
{ "error": "generation_failed", "detail": "orchestration_failed: ...", "refunded": 0.15 } // 502,已退款
|
|
73
|
+
{ "error": "invalid_api_key" } // 401
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 计费
|
|
77
|
+
|
|
78
|
+
| model | 单价 | 输出 | 备注 |
|
|
79
|
+
|---|---|---|---|
|
|
80
|
+
| `standard` | **$0.02** / 次 | 恒 1K(Agnes) | 草稿、迭代、批量试错 |
|
|
81
|
+
| `premium` | **$0.15** / 次 | 1K 或 2K **同价** | 终稿、正式交付、入 paper |
|
|
82
|
+
|
|
83
|
+
✅ 尺寸不影响价格 → premium 无脑用 2K。
|
|
84
|
+
✅ 每 key 关联账号,**只从余额扣**,与会员订阅额度独立。
|
|
85
|
+
✅ **生成失败自动原路退款**(含 RPM 429 场景)。
|
|
86
|
+
|
|
87
|
+
**RPM(每分钟请求数),与网页端共享同一窗口**:免费 5 · Lite 10 · Plus 15 · Pro 40 · Ultra 80。
|
|
88
|
+
未订阅 = 免费层 5 RPM,批量任务务必串行 + 退避。
|
|
89
|
+
|
|
90
|
+
### 调用示例
|
|
91
|
+
|
|
92
|
+
#### Bash / curl(默认 UA 不会被 WAF 拦)
|
|
93
|
+
|
|
94
|
+
🔴 **响应必须落文件,禁止把原始响应直接回显进对话**——`b64_json` 是几百 KB 的 base64
|
|
95
|
+
(数十万 token 级),直接打印轻则污染上下文、重则撑爆会话(2026-09-25 实测)。
|
|
96
|
+
照下面的范式:管道进文件,jq 只回显元数据字段。
|
|
97
|
+
|
|
98
|
+
🔴 **落盘一律用当前目录相对路径,禁用 `/tmp`**(2026-09-25 实测):Windows 下 Git Bash 的
|
|
99
|
+
`/tmp` 指向 AppData\Local\Temp,而 Windows 版 curl/python 把 `/tmp` 解析成 `<当前盘>:\tmp`——
|
|
100
|
+
两套解析混用会「写入成功但读不到」反复重试,最终交付物还会落在用户找不到的 `C:\tmp`。
|
|
101
|
+
中间产物和最终交付图都放当前工作目录。
|
|
102
|
+
|
|
103
|
+
🔴 **curl 必须显式 `--max-time 300`**:生成耗时 46s~160s+,很多宿主的 Bash 工具默认 120s
|
|
104
|
+
就掐断命令(2026-09-25 实测连续两次超时返工)。宿主有 timeout 参数的一并设到 300s+。
|
|
105
|
+
⚠️ **注意:`timeout 300 curl …` 救不了宿主工具级的 120s 掐断**——掐的是整个命令不是 curl。
|
|
106
|
+
宿主 Bash 工具支持 timeout 参数的(如 claude CLI)调用时必须显式传(如 `timeout: 300000`);
|
|
107
|
+
不支持的用后台模式 + 分次轮询:
|
|
108
|
+
|
|
109
|
+
🔴 **禁用宿主 Bash 工具自带的「后台任务」机制跑 curl**(claude CLI 的 run_in_background 等):
|
|
110
|
+
**会话一结束,未完成的任务直接被杀**——2026-09-25 实测 curl 被这样杀掉后 resp 文件根本没落地,
|
|
111
|
+
宿主却已向用户报告"已提交、预算 $0.04"(虚报)。要后台只允许 `nohup … &`(脱离会话存活)+
|
|
112
|
+
**轮询到 DONE 才能结束回合**;否则一律前台 curl。
|
|
113
|
+
|
|
114
|
+
🔴 **先核实后宣称**:向用户报告任何进度前必须核实证据——resp 文件存在(`ls`)、
|
|
115
|
+
JSON 合法(`jq '{size,model,crafted,charged,balance}' resp.json` 回显元数据)。
|
|
116
|
+
没核实禁止说「已提交 / 已在生成 / 已计费」。台账 `pf-ledger.md` 每次调用**立即补一行**,禁止空表。
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
nohup curl -s --max-time 300 -X POST https://promptfigure.top/api/v1/generate \
|
|
120
|
+
-H "Authorization: Bearer $PROMPTFIGURE_KEY" -H "Content-Type: application/json" \
|
|
121
|
+
-d @req.json -o resp.json > curl.log 2>&1 &
|
|
122
|
+
# 之后的工具调用里轮询(每次调用查一次,别在一个命令里 sleep 死等):
|
|
123
|
+
jq -e '.b64_json' resp.json > /dev/null && echo DONE || echo WAITING
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
gen() {
|
|
128
|
+
curl -s --max-time 300 -X POST https://promptfigure.top/api/v1/generate \
|
|
129
|
+
-H "Authorization: Bearer $PROMPTFIGURE_KEY" \
|
|
130
|
+
-H "Content-Type: application/json" \
|
|
131
|
+
-d "$1" -o resp.json
|
|
132
|
+
jq -r .b64_json resp.json | base64 -d > "${2:-figure.png}"
|
|
133
|
+
jq '{size,model,crafted,charged,balance,refIgnored}' resp.json
|
|
134
|
+
}
|
|
135
|
+
gen '{"prompt":"对比 ResTiNet 和 CNN 在 OCT 分类上的表现,左侧数据流右侧柱状图","model":"premium","ratio":"16:9"}' fig1.png
|
|
136
|
+
# 紧急绕过(平时不需要):末尾加 "polish":false,且 prompt 需自己写成完整英文专业提示词
|
|
137
|
+
# gen '{"prompt":"<完整英文提示词>","model":"premium","polish":false,"ratio":"16:9"}' fig1.png
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### Node(fetch 默认 UA 不过 WAF)
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
const B = "https://promptfigure.top";
|
|
144
|
+
const b64 = await fetch(B + "/api/v1/generate", {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: { Authorization: `Bearer ${process.env.PROMPTFIGURE_KEY}`,
|
|
147
|
+
"Content-Type": "application/json" },
|
|
148
|
+
body: JSON.stringify({ prompt, model: "premium", ratio: "16:9" }), // 正常:不传 polish
|
|
149
|
+
// 紧急绕过(平时不需要):加 polish: false,且 prompt 需写成完整英文专业提示词
|
|
150
|
+
}).then(r => r.json());
|
|
151
|
+
require("fs").writeFileSync("figure.png", Buffer.from(b64.b64_json, "base64"));
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Python `requests`(UA 安全,需 `pip install requests`)
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import os, base64, requests
|
|
158
|
+
r = requests.post(
|
|
159
|
+
"https://promptfigure.top/api/v1/generate",
|
|
160
|
+
headers={"Authorization": f"Bearer {os.environ['PROMPTFIGURE_KEY']}"},
|
|
161
|
+
json={"prompt": "对比 ResTiNet 和 CNN 在 OCT 分类上的表现,左侧数据流右侧柱状图",
|
|
162
|
+
"model": "premium", "ratio": "16:9"}, # 正常:不传 polish
|
|
163
|
+
# 紧急绕过(平时不需要):加 "polish": False,prompt 需写成完整英文专业提示词
|
|
164
|
+
timeout=300,
|
|
165
|
+
)
|
|
166
|
+
if not r.ok:
|
|
167
|
+
raise SystemExit(f"{r.status_code} {r.json()}")
|
|
168
|
+
d = r.json()
|
|
169
|
+
open("figure.png", "wb").write(base64.b64decode(d["b64_json"]))
|
|
170
|
+
print(d["charged"], d["balance"], d.get("refIgnored"))
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### Python `urllib.request`(⚠️ 必须改 UA,否则 403)
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
import json, base64, urllib.request
|
|
177
|
+
req = urllib.request.Request(
|
|
178
|
+
"https://promptfigure.top/api/v1/generate",
|
|
179
|
+
data=json.dumps({"prompt":"...","model":"premium","ratio":"16:9"}).encode(),
|
|
180
|
+
# 紧急绕过(平时不需要):dict 里加 "polish": False
|
|
181
|
+
headers={
|
|
182
|
+
"Authorization": f"Bearer {os.environ['PROMPTFIGURE_KEY']}",
|
|
183
|
+
"Content-Type": "application/json",
|
|
184
|
+
"User-Agent": "Mozilla/5.0", # 绕开 CF WAF 拦截
|
|
185
|
+
},
|
|
186
|
+
method="POST",
|
|
187
|
+
)
|
|
188
|
+
d = json.loads(urllib.request.urlopen(req, timeout=300).read())
|
|
189
|
+
open("figure.png", "wb").write(base64.b64decode(d["b64_json"]))
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 客户端 `timeout`
|
|
193
|
+
|
|
194
|
+
默认管线实测 42–90s;premium 2K 偶尔更久。**所有客户端 timeout 建议 ≥ 300s**。
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 路径 B:网页工作流等价的异步流程
|
|
199
|
+
|
|
200
|
+
完整 4 步(路径 B 走默认润色,无 polish 开关):
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
const B = "https://promptfigure.top";
|
|
204
|
+
const post = (p, b, t) => fetch(B+p, {
|
|
205
|
+
method:"POST",
|
|
206
|
+
headers:{"Content-Type":"application/json", Authorization:`Bearer ${t}`},
|
|
207
|
+
body:JSON.stringify(b),
|
|
208
|
+
}).then(r=>r.json());
|
|
209
|
+
|
|
210
|
+
// 1. 登录
|
|
211
|
+
const { token: tok } = await post("/api/login", {email, password});
|
|
212
|
+
|
|
213
|
+
// 2. 铸造 gen token(此处扣费)
|
|
214
|
+
// body: { token: tok, prompt, size: "1K"|"2K", pool: "premium"|null, ratio }
|
|
215
|
+
const { token: gt } = await post("/api/generate-token", {
|
|
216
|
+
token: tok,
|
|
217
|
+
prompt: "...",
|
|
218
|
+
size: "1K",
|
|
219
|
+
pool: null, // null=标准池;premium 显式传 "premium"
|
|
220
|
+
ratio: "16:9",
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// 3. 入队
|
|
224
|
+
const { jobId } = await post("/api/gen-async", { token: gt });
|
|
225
|
+
|
|
226
|
+
// 4. 轮询 /api/gen-result,status 序列:queued → polishing → imaging → qa → done(终态还有 error)
|
|
227
|
+
for (;;) {
|
|
228
|
+
const r = await post("/api/gen-result", { token: tok, id: jobId });
|
|
229
|
+
if (r.status === "done") return r.imageUrl; // 直接可用的图 URL
|
|
230
|
+
if (r.status === "error" || r.error) throw new Error(JSON.stringify(r));
|
|
231
|
+
await new Promise(s => setTimeout(s, 5000));
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
⚠️ 路径 B **没有等价 `polish:false` 开关**——上游限频期偏慢或失败时,急用请走路径 A。
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 一键临摹 `mode: "replica"`(2026-09-18)
|
|
240
|
+
|
|
241
|
+
**什么时候用**:用户给了一张现成的科研图(示意图 / 流程图 / 机制图 / 图形摘要 / 体系结构图),要求"照这个画一版 / 重画一张 / 保持结构一致",或者想把别人论文里那张图重做成自己的一套图。**这是这种需求的首选参数**,比自己揣摩着写 prompt 准得多。
|
|
242
|
+
|
|
243
|
+
**和普通参考图的区别**(关键,决定该不该用它):
|
|
244
|
+
|
|
245
|
+
| | 普通参考图(refUrl/refDataUrl) | 一键临摹(+ mode:"replica") |
|
|
246
|
+
|---|---|---|
|
|
247
|
+
| 参考图给谁 | 只给生图模型,提示词层不知道它存在 | 先送**视觉模型**读成结构化清单,再由清单驱动提示词与审查 |
|
|
248
|
+
| 保真依据 | 靠模型看图即兴 | 清单逐条比对(漏项/多项/编造都能判) |
|
|
249
|
+
| 适合 | 只借风格、构图、配色 | 要**结构一致**的复刻 |
|
|
250
|
+
|
|
251
|
+
**请求**(必须带参考图,`prompt` 可留空):
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
gen '{"mode":"replica","refUrl":"https://.../figure3.png","model":"premium","ratio":"16:9"}' replica.png
|
|
255
|
+
# prompt 也可以写要点,例如 {"mode":"replica","prompt":"改成中文标注","refUrl":"..."}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**响应里的 `referenceSpec` 就是那份清单**,可拿来核对本次出图:
|
|
259
|
+
|
|
260
|
+
```jsonc
|
|
261
|
+
{
|
|
262
|
+
"canvas": "16:9 landscape", // ⚠️ 视觉模型对该字段不稳定,仅供方向参考(真实比例由 ratio 决定)
|
|
263
|
+
"layout": "three phases stacked vertically, Phase II splits into 3 parallel columns",
|
|
264
|
+
"sections": [{ "id": "a", "role": "..." }],
|
|
265
|
+
"palette": [{ "role": "process steps", "color": "light green" }],
|
|
266
|
+
"text": [{ "s": "MAPE 8.3%", "kind": "label", "lang": "en", "readable": true }],
|
|
267
|
+
"elements": [{ "name": "Data preprocessing box", "kind": "box", "note": "green rounded" }],
|
|
268
|
+
"relations":["Data -> Raw data box", "Decision diamond -> ... (no/tighten constraints)"],
|
|
269
|
+
"photos": ["western blot panel"],
|
|
270
|
+
"ambiguities": ["exact arrow connectivity not individually drawn"]
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
- `text` 是**原样抄录**(不翻译、不纠错),出图的图上文字按它逐字渲染——所以它也是"中文/英文标注是否正确"的验收依据。`readable:false` 的条目会用中性占位,不会瞎猜。
|
|
275
|
+
- `photos` 里的区域会以**示意方式**重绘,不会伪造显微照片/电泳条带细节(科研场景伪造图像属于学术不端)。
|
|
276
|
+
|
|
277
|
+
**约束与错误码**:
|
|
278
|
+
|
|
279
|
+
| 情况 | 结果 |
|
|
280
|
+
|---|---|
|
|
281
|
+
| 没带参考图 | `400 replica_requires_reference`(不扣费) |
|
|
282
|
+
| 参考图 base64 后 >8MB | `413 replica_reference_too_large`(临摹要把图送视觉模型,上限比普通参考图紧;压缩/裁剪后重试) |
|
|
283
|
+
| 视觉轮失败 | 不报错:退化为"带图直接写提示词",`specError` 带原因,正常计费 |
|
|
284
|
+
| 润色失败 | `502 orchestration_failed` + **自动退款**(与普通生成同一闭环) |
|
|
285
|
+
|
|
286
|
+
**不适合临摹的**:照片/显微照片/电泳图**本身**(要的是保真像素,不是重画);数据图表里要精确到像素的坐标轴排布。
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## 批处理
|
|
291
|
+
|
|
292
|
+
必须串行 + 429 退避:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
import time, requests
|
|
296
|
+
def gen(prompt, **kw):
|
|
297
|
+
for attempt in range(4):
|
|
298
|
+
r = requests.post(URL, headers=H,
|
|
299
|
+
json={"prompt": prompt, **kw}, # 正常:不传 polish
|
|
300
|
+
timeout=300)
|
|
301
|
+
if r.status_code == 429:
|
|
302
|
+
time.sleep(2 ** attempt); continue
|
|
303
|
+
if r.status_code == 502:
|
|
304
|
+
time.sleep(1); continue # 已退款,可安全重试
|
|
305
|
+
r.raise_for_status()
|
|
306
|
+
return r.json()
|
|
307
|
+
raise RuntimeError("retry exhausted")
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
❌ 不要并发——RPM 是账号级窗口,并发只会换来 429,总吞吐不变。
|
|
311
|
+
✅ 预算有限时先跑 `standard` 看构图,定了再跑 `premium` 出终稿。
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## 不适合本 API 的场景
|
|
316
|
+
|
|
317
|
+
- 需要**精确数据绑定**的图表(要有真实 CSV 数值驱动)→ 用 matplotlib / ggplot 画更准确
|
|
318
|
+
- 超大分辨率打印级图(最高 2K)
|
|
319
|
+
- 严格可复现、像素级可控的排版
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# 文档插图工作流:在论文/报告里找到出图位置并生成配图
|
|
2
|
+
|
|
3
|
+
> 场景:用户给你一篇 `.tex` / `.docx` / `.md` 文稿,要求「把该配图的地方配上图」。本文件教你 **① 定位哪里该出图 ② 从上下文写出正确 prompt ③ 把图插回文档**。
|
|
4
|
+
>
|
|
5
|
+
> 批量升级已有图(结果图溯源重绘/示意图 AI 升级/整文批处理/追溯台账)见 `figure-upgrade-workflow.md`;PDF/WPS 解析与主动插图建议见 `proactive-upgrade.md`。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. 调研背景(2026-09-09,为什么不造轮子)
|
|
10
|
+
|
|
11
|
+
调研了 GitHub 上的现成方案,**没有一个开源项目完整做到「读文档 → 定插图位 → 调外部生图 API → 插回文档」**。最接近的:
|
|
12
|
+
|
|
13
|
+
| 项目 | 做了什么 | 对本技能的启示 |
|
|
14
|
+
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
|
|
15
|
+
| [paperfigg](https://github.com/oluwafemidiakhoa/paperfigg)(PyPI `paperfigg`,v0.4+) | 论文(PDF/MD) → agentic 规划→生成→审查 → 出图 + **LaTeX include snippet + caption + 图元素到原文 span 的溯源映射** | 借鉴它的 **figure plan**(先列图清单再逐张生成)和 **caption 从原文生成** 思路;但它用自家生成器,不接外部 API |
|
|
16
|
+
| [scitex-writer](https://github.com/SciTeX-AI/scitex-writer) | LaTeX 稿件管理 MCP server(`figures add fig01 plot.png "Caption"` 等 44 工具),管插图/编译不管生成 | 插回 LaTeX 的命令式做法可参考;需要完整稿件工程,太重 |
|
|
17
|
+
| [DeTikZify](https://github.com/potamides/DeTikZify)(NeurIPS 2024 spotlight) | 多模态模型把草图/已有图/文本 caption 合成 TikZ 矢量图 | 「caption→图」方向的学术标杆;要本地 GPU + TeX Live,不适合直接集成 |
|
|
18
|
+
| [paper-figure(kitcaf)](https://github.com/kitcaf/skills)、AutoResearchClaw、ARIS 等 | 数据图(matplotlib 级)+ `PAPER_PLAN.md` 图规划;**明确承认架构图/机制图自动生成质量不行** | 数据图走本地脚本更省;**概念图/机制图/管线图正是 promptFigure 的强项**——两者互补不冲突 |
|
|
19
|
+
| docx 侧 | 只有 python-docx 机械插图的 skill(如 `vamseeachanta/workspace-hub` 的 image-insertion),**「哪里该插图」的决策完全空白** | 定位逻辑由本文件 §1 提供 |
|
|
20
|
+
|
|
21
|
+
结论:位置决策 + prompt 构造按本文件执行;不做通用工具,让 AI 现场判断。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. 定位:哪里该出图
|
|
26
|
+
|
|
27
|
+
### LaTeX(.tex)
|
|
28
|
+
|
|
29
|
+
按优先级扫描这些信号(`grep -n` 即可):
|
|
30
|
+
|
|
31
|
+
> **用户给的不是 LaTeX 而是 PDF/WPS/Word 稿**:已有插图优化、插图位主动建议、从原始数据推演配图,
|
|
32
|
+
> 走 `proactive-upgrade.md` 的四步管线(PDF 解析 → MCM 插图位惯例 → 提示词 → 迭代优化);
|
|
33
|
+
> 本节信号表面向 LaTeX 源。
|
|
34
|
+
|
|
35
|
+
| 信号 | 含义 | 动作 |
|
|
36
|
+
| ------------------------------------------------------------------------------- | ----------- | ----------------------- |
|
|
37
|
+
| `\begin{figure}...\end{figure}` 空壳或缺 `includegraphics` | 作者留了图位 | **必插** |
|
|
38
|
+
| `% TODO: figure` / `%% FIGURE HERE` 类注释 | 明确占位 | **必插** |
|
|
39
|
+
| 正文有 `如图~\ref{fig:xxx}` / `as shown in Figure~\ref{...}` 但 `\label{fig:xxx}` 不存在 | 引用了不存在的图 | **必插**(label 用引用处的 key) |
|
|
40
|
+
| 无任何图引用 | 需要判断要不要建议插图 | 见下方「章节启发式」 |
|
|
41
|
+
|
|
42
|
+
**章节启发式**(无显式占位时,按科研论文惯例推荐插图位):
|
|
43
|
+
|
|
44
|
+
- **引言/摘要末** → 图形摘要(graphical abstract)或 teaser 总览图,1 张,覆盖全文核心流程
|
|
45
|
+
- **方法/模型章节** → 架构图、管线图、机制示意图(每小节最多 1 张,总 ≤3)
|
|
46
|
+
- **实验设置** → 数据集/实验流程示意(可选)
|
|
47
|
+
- **结果分析** → **数据图优先用本地 matplotlib/Excel 出**(paper-figure 类工具已覆盖),只有「对比关系示意」这类概念图才值得用本 API
|
|
48
|
+
|
|
49
|
+
### 1.5 判定标准:这段文字配不配得上一张图
|
|
50
|
+
|
|
51
|
+
占位符信号(必插)之外,无占位段落**只有 4 类正当理由**该配图——图的唯一使命是承载文字承载不了的信息:
|
|
52
|
+
|
|
53
|
+
| 类型 | 文字判据 | 典型图 |
|
|
54
|
+
| -------- | ------------------------------------- | ---------------- |
|
|
55
|
+
| **结构拓扑** | 段内 ≥3 个实体 + 方向/连接词(送入、融合、拼接、输出、反馈、级联) | 管线/架构/流程图 |
|
|
56
|
+
| **空间形态** | 在描述「长什么样、在哪、怎么连」而非「为什么」 | 通路位置、几何/布局示意 |
|
|
57
|
+
| **概念对比** | 多组对象的**定性**差异(不含精确数值) | 方法 A vs B 流程差异示意 |
|
|
58
|
+
| **全文压缩** | 读者需 30 秒理解全文 | 图形摘要 / teaser |
|
|
59
|
+
|
|
60
|
+
口诀:**三个实体手拉手、文字读三遍才拼出结构 → 画;数字支撑 → 本地画;都没占 → 不画。**
|
|
61
|
+
|
|
62
|
+
反向排除(不该加图):
|
|
63
|
+
|
|
64
|
+
- ❌ **带精确数值的结果图**(曲线、柱状、热图)→ 一律本地 matplotlib——AI 生图必画错数字,坐标数据必须是真数据
|
|
65
|
+
- ❌ 1-2 个实体、一句话说得清的关系 → 凑数
|
|
66
|
+
- ❌ 定义/假设/证明类纯逻辑段 → 装饰
|
|
67
|
+
- ❌ 该信息已有别的图覆盖 → 重复
|
|
68
|
+
|
|
69
|
+
### Word(.docx)
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from docx import Document
|
|
73
|
+
doc = Document("paper.docx")
|
|
74
|
+
for i, p in enumerate(doc.paragraphs):
|
|
75
|
+
t = p.text
|
|
76
|
+
if any(k in t for k in ("如图", "见图", "如上图", "如下图", "Figure", "Fig.", "图X", "【图")):
|
|
77
|
+
print(i, p.style.name, t[:80])
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- 命中「如图 X 所示 / Figure X」且附近无图片段落 → 该处插入
|
|
81
|
+
- 中文论文常写「(此处插入图 X)」或用「图 X」独立行占位 → 直接替换
|
|
82
|
+
- 结构启发式同 LaTeX:「方法」章 → 机制图;「摘要」后 → 图形摘要
|
|
83
|
+
|
|
84
|
+
### Markdown
|
|
85
|
+
|
|
86
|
+
`![placeholder]`、\`\`、`**[图 X]**` 等占位,逻辑同上。
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 2. prompt 怎么写:从上下文提取,不凭空编
|
|
91
|
+
|
|
92
|
+
**铁律不变(见 SKILL.md)**:说清「图种 + 实体 + 结构」即可,扩写交给服务端润色。关键是从文档里**提取真实实体**,而不是写通用模板句。
|
|
93
|
+
|
|
94
|
+
### 2.0 上下文蒸馏三步法(🔴 原文段落绝不直接进 prompt)
|
|
95
|
+
|
|
96
|
+
**为什么**:把插图位周围的成段原文塞给生图 API 是低效甚至有害的——润色层会被叙述性文本淹没(论点、引用、过渡句全变成噪声),重点稀释后实体和结构反而抓不准,长文本还挤占 prompt 预算。上下文的价值在于**给 Agent 提炼,不是给 API 阅读**。
|
|
97
|
+
|
|
98
|
+
三步走:
|
|
99
|
+
|
|
100
|
+
1. **提取**(本地):把插图位前后 2-3 段原文 + 章节标题 + 现有 caption 抄到你的工作区
|
|
101
|
+
2. **蒸馏**(本地):从原文提炼成三份结构化清单——**实体清单**(模块/方法名,原词)、**结构清单**(数据流方向/并行/汇合)、**图种判定**(按 §1.5)
|
|
102
|
+
3. **写 prompt**:只把三份清单按 golden skeleton(`prompt-review-workflow.md` 阶段 1)组装成 prompt——组装产物里**不应有任何一句完整的原文句子**
|
|
103
|
+
|
|
104
|
+
**蒸馏 vs 粘贴(正反例)**:
|
|
105
|
+
|
|
106
|
+
- ❌ 粘贴:`prompt: "论文写道:编码器提取特征后经跨尺度融合模块送入解码器,并与低层细节特征拼接,该方法在三个数据集上均取得最优精度……"`(叙述、结果句、引用全进了 prompt)
|
|
107
|
+
- ✅ 蒸馏:`prompt: "模型管线图。Encoder → Cross-scale Fusion Module(含低层 skip 拼接)→ Decoder,箭头标数据流,模块名英文原样,扁平矢量白底"`(只有实体+结构+风格)
|
|
108
|
+
|
|
109
|
+
### 2.1 上下文取材范围
|
|
110
|
+
|
|
111
|
+
**上下文取材优先级**:插图位前后 2-3 段 > 章节标题层级 > 全文摘要 > 用户口头描述。够不到的用占位符,不反问。
|
|
112
|
+
|
|
113
|
+
**只取与图有关的内容**:模块名、数据流方向词(送入/融合/拼接/级联)、对比对象、分组关系。**舍弃**:论点句、实验结论、引用文献、过渡句——这些进了 prompt 只会让图上长出垃圾文字。
|
|
114
|
+
|
|
115
|
+
### 2.2 给 API 的上下文清单(最小充分集 3 项 + 建议集 4 项)
|
|
116
|
+
|
|
117
|
+
API 的润色层需要的是**意图**,不是成稿 prompt。一次性提交按此清单收敛(零反问):
|
|
118
|
+
|
|
119
|
+
**必给 3 项(缺一图必歪):**
|
|
120
|
+
|
|
121
|
+
| # | 项 | 来源 | 没有时 |
|
|
122
|
+
| - | -------------------------------------------- | ----------- | -------------------------- |
|
|
123
|
+
| 1 | **图种**:管线 / 机制 / 对比 / 框架 / 图形摘要 | §1.5 判定类型 | 从段落动词推断(「送入/融合」→管线) |
|
|
124
|
+
| 2 | **实体清单**:方法名、模块名**原样搬运**(拼写不改),实体数 ≈ panel 数 | 插图位前后 2-3 段 | 占位符(`Module A / Module B`) |
|
|
125
|
+
| 3 | **关系结构**:谁指向谁、分几组、左右/上下、哪条是 skip/反馈/级联 | 段内方向词 | 默认从左到右单向流 |
|
|
126
|
+
|
|
127
|
+
**建议给 4 项(有推定默认,给了更准):**
|
|
128
|
+
|
|
129
|
+
| # | 项 | 推定规则(不给时) |
|
|
130
|
+
| - | ----------------------------------------------- | ---------------------- |
|
|
131
|
+
| 4 | 视觉角色:哪个模块是核心贡献(强调色) | 章节主题词 ≈ 核心模块 |
|
|
132
|
+
| 5 | caption 一句话(润色层的锚点) | 从插图位段落首句压缩 |
|
|
133
|
+
| 6 | 版面 ratio:单栏 `3:2`/`1:1`,跨栏(`figure*`)/全宽 `16:9` | 按 LaTeX 单双栏或 Word 页宽推 |
|
|
134
|
+
| 7 | 档位:投稿/对外展示 `premium`,工作稿/草稿迭代 `standard` | 看文稿状态(预印本/投稿版→premium) |
|
|
135
|
+
|
|
136
|
+
**上下文取材优先级**:插图位前后 2-3 段 > 章节标题层级 > 全文摘要 > 用户口头描述。够不到的用占位符,不反问。
|
|
137
|
+
|
|
138
|
+
### 2.3 提取步骤
|
|
139
|
+
|
|
140
|
+
1. **读插图位前后各 2-3 段**,提取:方法/模块名(原样保留拼写,如 ResTiNet、GPX4)、模块间数据流方向、对比对象、关键数值/指标名
|
|
141
|
+
2. **定图种**(对照 `prompt-cookbook.md` 的五类模板):流程/管线 → pipeline 模板;机制/通路 → mechanism 模板;多方法对比 → comparison 模板;技术路线 → framework 模板
|
|
142
|
+
3. **prompt 里写清楚 caption 承担不了的信息**:图不重复正文文字,要补「结构」——谁指向谁、分几组、左还是右
|
|
143
|
+
4. **比例按版面定**:LaTeX 单栏图 `3:2` 或 `1:1`;跨栏/双栏跨度(`figure*`)用 `16:9`;Word 全宽 `16:9`,半宽 `3:2`
|
|
144
|
+
5. 图形摘要/teaser → `premium` 档(对外展示,见 SKILL.md 档位表);方法章节工作稿 → `standard`
|
|
145
|
+
6. **字体按规范写进 prompt**:无衬线正向 + 手写/花体禁令,见 `prompt-review-workflow.md` 字体规范节
|
|
146
|
+
|
|
147
|
+
**一个从上下文到 prompt 的实例**(方法章写道「编码器提取特征后经跨尺度融合模块送入解码器,并与低层细节特征拼接」):
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{"prompt": "论文方法章配图:模型整体管线图。三个模块从左到右:Encoder(提取特征)→ Cross-scale Fusion Module(跨尺度融合,含来自低层的 skip 连接拼接)→ Decoder(输出预测)。用箭头标注数据流,模块名按英文原样标注 Encoder / Cross-scale Fusion / Decoder。扁平矢量风,白底。", "model": "standard", "ratio": "16:9"}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
要点:模块名**原样搬运自文档**(不翻译不改写)、数据流方向照正文、结构(skip 拼接)显式写出。
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 3. 插回文档
|
|
158
|
+
|
|
159
|
+
### LaTeX
|
|
160
|
+
|
|
161
|
+
```latex
|
|
162
|
+
\begin{figure}[t]
|
|
163
|
+
\centering
|
|
164
|
+
\includegraphics[width=\linewidth]{figures/pipeline.png}
|
|
165
|
+
\caption{Overall architecture of the proposed method.(从对应段落一句概括,照 paperfigg 的做法 caption 承担「图在说什么」,不重复正文)}
|
|
166
|
+
\label{fig:pipeline} % 若正文已用 \ref{fig:xxx} 引用,label 必须用同一个 key
|
|
167
|
+
\end{figure}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- 保存图到 `figures/`(与 `\graphicspath` 一致);PNG 直接可用
|
|
171
|
+
- 位置参数:方法章机制图用 `[t]`(页顶),紧跟首次引用段落之后声明
|
|
172
|
+
|
|
173
|
+
### Word(.docx,python-docx)
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from docx import Document
|
|
177
|
+
from docx.shared import Inches, Pt
|
|
178
|
+
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
179
|
+
|
|
180
|
+
doc = Document("paper.docx")
|
|
181
|
+
target = doc.paragraphs[12] # §1 定位到的「如图 X 所示」段落
|
|
182
|
+
|
|
183
|
+
# 图片段:插在 target 之前
|
|
184
|
+
img_p = target.insert_paragraph_before()
|
|
185
|
+
img_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
186
|
+
img_p.add_run().add_picture("pipeline.png", width=Inches(5.5)) # 全宽约 5.5-6.0in
|
|
187
|
+
|
|
188
|
+
# 图注段:再插一次,正好落在图片之后、原文段落之前
|
|
189
|
+
cap_p = target.insert_paragraph_before()
|
|
190
|
+
cap_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
191
|
+
r = cap_p.add_run("图 1:模型整体管线示意")
|
|
192
|
+
r.italic = True
|
|
193
|
+
r.font.size = Pt(10)
|
|
194
|
+
|
|
195
|
+
doc.save("paper.docx")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
(图注惯例:图片下方居中,「图 1:说明」,10pt 斜体;插图后全文图号需人工复核顺延。)
|
|
199
|
+
|
|
200
|
+
### Markdown
|
|
201
|
+
|
|
202
|
+
```markdown
|
|
203
|
+

|
|
204
|
+
*图 1:Encoder → Cross-scale Fusion → Decoder 的数据流。*
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 3.5 LaTeX 编译环境:用户没有时怎么办
|
|
208
|
+
|
|
209
|
+
先探测(`pdflatex --version` / `xelatex --version` / `latexmk` / `tectonic`)。**探测不到就问用户**「论文平时在哪里编译」(可能在本机别处、其他机器或 Overleaf),别擅自装重型工具链;用户确实想在本机新装时,按平台给官方下载指引:
|
|
210
|
+
|
|
211
|
+
| 方案 | 平台 | 链接 | 适合 |
|
|
212
|
+
|---|---|---|---|
|
|
213
|
+
| **MiKTeX** | Windows/macOS | https://miktex.org/download | Windows 首选,按需自动装宏包 |
|
|
214
|
+
| **TeX Live** | 全平台 | https://tug.org/texlive/ | 最完整,体积大(数 GB) |
|
|
215
|
+
| **TinyTeX** | 全平台 | https://yihui.org/tinytex/ | 轻量(百 MB 级),命令行安装,适合自动化 Agent:`wget -qO- "https://yihui.org/tinytex/install-bin-unix.sh" | sh`(Windows 用 `install-bin-windows.bat`) |
|
|
216
|
+
| **Tectonic** | 全平台 | https://tectonic-typesetting.github.io | 单二进制 + 自动拉依赖包,编译:`tectonic main.tex`;Windows `winget install TectonicTypesetting.Tectonic` 或 `scoop install tectonic` |
|
|
217
|
+
| **Overleaf** | 在线 | https://www.overleaf.com | 零安装,把工程 zip 传上去编译;无本地权限时的默认答案 |
|
|
218
|
+
|
|
219
|
+
装好后的标准编译序(含参考文献):`pdflatex main → bibtex main → pdflatex main ×2`,或一条 `latexmk -pdf main`。CVPR 这类模板用 `xelatex` 时把 `pdflatex` 换成 `xelatex` 即可。
|
|
220
|
+
|
|
221
|
+
Agent 行为规范:安装动作**必须先征得用户同意**再执行;用户选 Overleaf 就把工程打包成 zip 交付并说明上传步骤。
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 4. 完整流程清单
|
|
226
|
+
|
|
227
|
+
1. 扫描文档 → 按 §1 列出**figure plan**(位置 + 每张的图种 + 从上下文提取的实体)——先给用户看清单再批量出图(这一步可以问,出图不问)
|
|
228
|
+
2. 逐张构造 prompt(§2)→ 调 `/api/v1/generate` → base64 存 PNG
|
|
229
|
+
3. **打开图片自查**(AI 有视觉能力就看一眼:文字乱码/实体拼写/结构对不对得上原文),不对就改 prompt 重出
|
|
230
|
+
4. 按版式插回(§3)+ 生成 caption
|
|
231
|
+
5. 汇报:每张图的「文档位置 → 图文件 → caption」对照表,提示用户全文图号/交叉引用需最终编译或人工复核
|