promptfigure 0.2.0 → 0.3.1

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.
Files changed (44) hide show
  1. package/README.md +29 -15
  2. package/adapters/claude-code/install.mjs +7 -6
  3. package/adapters/claude-code/promptfigure-api/INSTALL.md +45 -0
  4. package/adapters/claude-code/promptfigure-api/SKILL.md +291 -0
  5. package/adapters/claude-code/promptfigure-api/references/api-contract.md +319 -0
  6. package/adapters/claude-code/promptfigure-api/references/deliverables-ledger.md +119 -0
  7. package/adapters/claude-code/promptfigure-api/references/document-workflow.md +231 -0
  8. package/adapters/claude-code/promptfigure-api/references/figure-upgrade-workflow.md +201 -0
  9. package/adapters/claude-code/promptfigure-api/references/proactive-upgrade.md +120 -0
  10. package/adapters/claude-code/promptfigure-api/references/prompt-cookbook.md +223 -0
  11. package/adapters/claude-code/promptfigure-api/references/prompt-review-workflow.md +265 -0
  12. package/adapters/claude-code/promptfigure-api/references/setup-guide.md +151 -0
  13. package/adapters/claude-code/promptfigure-api/references/troubleshooting.md +204 -0
  14. package/adapters/claude-code/{SKILL.md → promptfigure-local/SKILL.md} +31 -0
  15. package/adapters/codex/promptfigure/.codex-plugin/plugin.json +2 -2
  16. package/adapters/codex/promptfigure/skills/promptfigure-api/INSTALL.md +45 -0
  17. package/adapters/codex/promptfigure/skills/promptfigure-api/SKILL.md +291 -0
  18. package/adapters/codex/promptfigure/skills/promptfigure-api/references/api-contract.md +319 -0
  19. package/adapters/codex/promptfigure/skills/promptfigure-api/references/deliverables-ledger.md +119 -0
  20. package/adapters/codex/promptfigure/skills/promptfigure-api/references/document-workflow.md +231 -0
  21. package/adapters/codex/promptfigure/skills/promptfigure-api/references/figure-upgrade-workflow.md +201 -0
  22. package/adapters/codex/promptfigure/skills/promptfigure-api/references/proactive-upgrade.md +120 -0
  23. package/adapters/codex/promptfigure/skills/promptfigure-api/references/prompt-cookbook.md +223 -0
  24. package/adapters/codex/promptfigure/skills/promptfigure-api/references/prompt-review-workflow.md +265 -0
  25. package/adapters/codex/promptfigure/skills/promptfigure-api/references/setup-guide.md +151 -0
  26. package/adapters/codex/promptfigure/skills/promptfigure-api/references/troubleshooting.md +204 -0
  27. package/adapters/codex/promptfigure/skills/promptfigure-local/SKILL.md +31 -0
  28. package/bin/pf.mjs +37 -0
  29. package/package.json +2 -2
  30. package/scripts/build-adapters.mjs +31 -18
  31. package/skill/promptfigure-api/INSTALL.md +45 -0
  32. package/skill/promptfigure-api/SKILL.md +291 -0
  33. package/skill/promptfigure-api/references/api-contract.md +319 -0
  34. package/skill/promptfigure-api/references/deliverables-ledger.md +119 -0
  35. package/skill/promptfigure-api/references/document-workflow.md +231 -0
  36. package/skill/promptfigure-api/references/figure-upgrade-workflow.md +201 -0
  37. package/skill/promptfigure-api/references/proactive-upgrade.md +120 -0
  38. package/skill/promptfigure-api/references/prompt-cookbook.md +223 -0
  39. package/skill/promptfigure-api/references/prompt-review-workflow.md +265 -0
  40. package/skill/promptfigure-api/references/setup-guide.md +151 -0
  41. package/skill/promptfigure-api/references/troubleshooting.md +204 -0
  42. package/skill/promptfigure-local/SKILL.md +31 -0
  43. package/web/app.js +9 -3
  44. package/web/style.css +5 -0
@@ -0,0 +1,151 @@
1
+ # 拿到 promptFigure API key
2
+
3
+ 全程站点:https://promptfigure.top
4
+ 注册 **不需要邮箱验证码**,邮箱 + 密码(≥8 位)即可。
5
+
6
+ **主路径:网页**。`/api/login`、`/api/keys` 等内部接口**面向已登录会话**,curl 注册可用但绕过了 UI(且会受 WAF UA 拦截),建议人肉注册、AI 只接管「登录后拿 key」这一步。
7
+
8
+ ---
9
+
10
+ ## 路径 A:网页人肉(小白 / 默认推荐)
11
+
12
+ 最稳。UI 自带 401/校验/防风控。
13
+
14
+ 1. 打开 https://promptfigure.top
15
+ 2. 右上角点 **注册**(或 登录)。填邮箱 + 密码(≥8 位),提交即完成,**无需验证邮件**
16
+ 3. 打开 https://promptfigure.top/console#account-balance 充值($1 起、必须整数,上限 $10000;每满 $50 赠 $1 进余额——按 `floor(金额/50)` 计算,**零头不累计**:$99 只赠 $1,$100 赠 $2)
17
+ 4. 打开 https://promptfigure.top/console#account-keys → 点创建 → **复制明文 key(`pf_` 开头),立刻存好**
18
+ 5. 把 key 交给调用方环境变量:
19
+
20
+ ```bash
21
+ export PROMPTFIGURE_KEY=pf_xxxxxxxx
22
+ ```
23
+
24
+ ⚠️ key 是 **会话内一次性明文**,弹窗关闭后只能看到 hint。库内只存 SHA-256 哈希 + hint。
25
+ ⚠️ SPA 必须带 hash 直达标签页:`#account-keys` / `#account-balance` 缺一不可,否则落总览页。
26
+
27
+ ---
28
+
29
+ ## 路径 B:浏览器自动化(有桌面/浏览器控制能力的 Agent)
30
+
31
+ ⚠️ **仅当路径 A 因浏览器限制不可用时用**。注册走 UI 风险大于收益,下面这版只接管「登录 + 拿 key」。
32
+
33
+ 注册(请用户自己点一次 UI):
34
+ - 邮箱 + 密码注册一次拿 `pf_` key 即可
35
+ - 注册后只登录会话:弹窗顶部是**分段控件(segmented-control),「登录」「注册」两个 tab 按钮并排**——点「登录」按钮切到 login tab → 填 email/password → 提交。**没有「已有账号?」这类链接文案**,自动化时按 segmented-control 内的按钮文本(登录 / Login)定位
36
+
37
+ 登录后的 DOM 选择器(实测有效):
38
+
39
+ | 元素 | 选择器 |
40
+ |---|---|
41
+ | 邮箱输入 | `#lite-email` |
42
+ | 密码输入 | `#lite-password` |
43
+ | 登录/注册弹窗关闭 | `.modal-close` |
44
+ | 提交按钮 | `.primary-action` |
45
+ | API 密钥标签页 | 访问 `/console#account-keys` |
46
+ | 余额标签页 | 访问 `/console#account-balance` |
47
+ | 调试台 key 输入框 | `#pf-play-key` |
48
+ | 调试台 prompt 输入框 | `#pf-play-prompt` |
49
+
50
+ 要点:
51
+ - 登录入口是**右上角弹窗**,不是独立登录页——需先点触发再操作表单
52
+ - 弹窗分 登录 / 注册 两个 tab,注册按钮在同一弹窗内切换(`mode` 状态)
53
+ - SPA 路由走 `history.pushState`,自动化时应直接导航完整 URL(含 hash),不要依赖点击导航
54
+ - 创建 key 后明文弹窗通常带「复制」按钮,优先点复制;若需读取文本,直接读弹窗 DOM 文本而不用 OCR
55
+ - 浏览器 fetch 不会被 CF WAF 拦截(只有默认 UA 才拦)
56
+
57
+ ---
58
+
59
+ ## 路径 C:纯 curl(无 UI 场景 / 自建脚本)
60
+
61
+ ⚠️ 注册/登录/建 key 都是内部 REST 接口,**没文档承诺稳定**——产品主路径是网页。这条路径出问题时,请优先回退到路径 A。
62
+
63
+ ### 客户端 UA(必经)
64
+
65
+ CF WAF 会拦 `Python-urllib/*`(实测 403 error code:1010)。其它都过。**curl 默认 UA 不在拦截名单里,可以直接用**;Python `requests` 默认 UA 也能过;只有 `urllib.request` 需要手动改 UA:
66
+
67
+ ```python
68
+ import urllib.request
69
+ req = urllib.request.Request(url, ...)
70
+ req.add_header("User-Agent", "Mozilla/5.0") # 绕过 CF WAF 拦截
71
+ ```
72
+
73
+ ### 1. 注册(可选,有账号则跳到 2)
74
+
75
+ ```bash
76
+ curl -s -X POST https://promptfigure.top/api/register \
77
+ -H "Content-Type: application/json" \
78
+ -d '{"email":"you@example.com","password":"至少8位"}'
79
+ ```
80
+
81
+ 成功返回 `201` + `{ token, userId, email, balance: 0, ... }`。
82
+ `409 email_taken` → 跳步骤 2 直接登录。
83
+
84
+ ### 2. 登录
85
+
86
+ ```bash
87
+ curl -s -X POST https://promptfigure.top/api/login \
88
+ -H "Content-Type: application/json" \
89
+ -d '{"email":"you@example.com","password":"你的密码"}'
90
+ ```
91
+
92
+ 返回 `{ token, userId, email, balance, credits, ... }`,token 是 session token(30 天有效)。
93
+
94
+ ### 3. 创建 API key
95
+
96
+ ```bash
97
+ export PF_TOKEN=<上一步的 token>
98
+ curl -s -X POST https://promptfigure.top/api/keys \
99
+ -H "Authorization: Bearer $PF_TOKEN" \
100
+ -H "Content-Type: application/json" \
101
+ -d '{"name":"my-agent"}'
102
+ ```
103
+
104
+ 返回 `{ id, key:"pf_...", hint, name, createdAt }`。立刻存:
105
+
106
+ ```bash
107
+ export PROMPTFIGURE_KEY=pf_xxxxxxxxxxxxxxxx
108
+ ```
109
+
110
+ ⚠️ **明文只在此响应中出现一次**,库里只存 SHA-256 哈希 + hint。丢了无法找回,只能吊销重建。
111
+
112
+ ### 4. 充值(必须步骤)
113
+
114
+ API 计费**只从余额扣**,与会员额度完全独立。新注册余额为 0,不充值调用会 `402`。
115
+ 充值走网页:https://promptfigure.top/console#account-balance($1 起,整数)。
116
+ **付款动作不应由 Agent 代劳**,留给用户人肉操作。
117
+
118
+ ### 5. 验证
119
+
120
+ ```bash
121
+ curl -s -X POST https://promptfigure.top/api/v1/generate \
122
+ -H "Authorization: Bearer $PROMPTFIGURE_KEY" \
123
+ -H "Content-Type: application/json" \
124
+ -d '{"prompt":"Simple two-group bar chart comparing method A and method B","model":"standard"}' \
125
+ | jq '{size, ratio, model, crafted, charged, balance}'
126
+ ```
127
+
128
+ 看到 `crafted: true` + `charged: 0.02` + `balance` 减少 → 打通(默认管线实测约 40–90 秒;若 502 看 `references/troubleshooting.md`)。
129
+
130
+ ---
131
+
132
+ ## key 管理
133
+
134
+ ```bash
135
+ # 列表(不含明文,只有 hint)
136
+ curl -s https://promptfigure.top/api/keys -H "Authorization: Bearer $PF_TOKEN"
137
+ # 吊销
138
+ curl -s -X POST https://promptfigure.top/api/keys/revoke \
139
+ -H "Authorization: Bearer $PF_TOKEN" \
140
+ -H "Content-Type: application/json" -d '{"id":"<key id>"}'
141
+ ```
142
+
143
+ 每人最多 10 把未吊销 key。
144
+
145
+ ---
146
+
147
+ ## 安全提示
148
+
149
+ - key 不进代码仓库、不进 commit。放环境变量或 secrets store
150
+ - 怀疑泄露先吊销再重建,成本为零
151
+ - 一次性不要创建过多 key;按用途命名(如 `paper-agent`、`lab-batch`)便于追溯
@@ -0,0 +1,204 @@
1
+ # 常见问题与排障
2
+
3
+ ## 错误码速查
4
+
5
+ | 码 | error 值 | 原因 | 处置 |
6
+ |---|---|---|---|
7
+ | 400 | `prompt_required` / `prompt_too_long` | prompt 空或 >8000 字符 | 检查请求体 |
8
+ | 401 | `invalid_api_key` | key 无效/已吊销/漏了 `Bearer ` 前缀 | 检查 header;无效则重建 key |
9
+ | 402 | `insufficient_balance` | 余额不足 | 控制台充值($1 起,整数) |
10
+ | 403 | `error code: 1010` | **CF WAF 拦了客户端 UA**(仅 `Python-urllib/*` 默认 UA) | 切 Node / Go / Python `requests` / 浏览器;或给 urllib 加 `User-Agent: Mozilla/5.0` |
11
+ | 429 | `rate_limited` | 超 RPM | 已退款,串行 + 退避重试(1s → 2s → 4s) |
12
+ | 502 | `generation_failed` | 生图通道异常 | **已自动退款**,看 `detail` 后重试;最常见是 `orchestration_failed: empty or truncated polish output` |
13
+ | 405 | `method_not_allowed` | 用了 GET | 必须 POST |
14
+
15
+ > 注意 RPM 限制是**账号级**,与网页端工作台共享同一个分钟窗口。网页上刚连续出过图,API 立刻报 429 属正常。
16
+
17
+ ---
18
+
19
+ ## 🔧 502 `orchestration_failed: empty or truncated polish output`
20
+
21
+ **上游 Agnes 文本模型间歇性限频**。限频期间默认管线可能 502(约 35 秒后返回),`refunded` 标明退还金额,无需补款。服务端已内置回落:主模型 agnes-2.5-flash 撞 429 时会自动用 agnes-2.0-flash 整体重试一次;两层都撞上才会 502。
22
+
23
+ ⚠️ 重要:**这是上游运维状态,不是产品形态。** `/api/v1/generate` 的设计就是默认走完整润色管线,与网页同款。不要把 `polish:false` 当常态。
24
+
25
+ ### 真实根因(2026-09-09 实证,已修订)
26
+
27
+ 上游返回 **`429 "Too many requests. Please try again in a moment."`**——**与本方用量完全无关**(实测窗口内仅 7 次调用 / 配额 7500,依然 429;数小时后自行恢复)。重要线索:同 key 同模型从本机直连不限流、只有站点出口被限 → **疑似 Agnes 按出口 IP 频控**,换模型未必躲得开,等服务端回落+稍后重试是正解。
28
+
29
+ **已修复的误导**:此前四种不同失败(配额耗尽/key 冷却/上游空响应/被上游拒绝)统一报成 `empty or truncated polish output`。**现在错误 detail 里带 `last_text_failure:` 字段**,直接给出上游真实状态码和响应片段——看到它就不用再猜。
30
+
31
+ ### 如何区分「平台故障」vs「我自己的问题」
32
+
33
+ | 特征 | 平台限频(本次) | 自己的问题 |
34
+ |---|---|---|
35
+ | 错误 detail | 含 `upstream 429` | 含 `prompt`/参数类提示 |
36
+ | 耗时 | 恒定 ~35s(重试耗尽) | 秒级返回 |
37
+ | `polish:false` 同参数重试 | ✅ 能出图 | 也失败 |
38
+ | 隔一段时间重试 | ✅ 自愈 | 仍失败 |
39
+
40
+ ### 处置
41
+
42
+ **首选:等几分钟到几小时后原样重试**(限频会自行解除;2026-09-09 实测当天恢复,默认管线 42–90s 成功出图)。
43
+
44
+ 赶时间时的临时绕过:
45
+
46
+ ```json
47
+ { "prompt": "<完整英文提示词>", "polish": false, "model": "standard" }
48
+ ```
49
+
50
+ ⚠️ `polish:false` 时服务端**不润色不扩写**,prompt 原样进图模型——所以必须自己写完整英文提示词(写法见 `prompt-cookbook.md` 的「降级模式」)。
51
+
52
+ ### 何时回到正常
53
+
54
+ 无需操作——限频解除后默认管线自动恢复(响应里 `crafted: true`)。`polish:false` 仅作为绕过手段保留。
55
+
56
+ ---
57
+
58
+ ## 🔴 CF WAF 403 `error code: 1010`
59
+
60
+ 实测拦截列表(2026-09-09):
61
+
62
+ | 客户端 | UA | 结果 |
63
+ |---|---|---|
64
+ | curl | `curl/7.x` | ✅ 200 |
65
+ | Node fetch | `node` | ✅ 200 |
66
+ | Python `requests` | `python-requests/2.x` | ✅ 200 |
67
+ | Go `net/http` | `Go-http-client/1.1` | ✅ 200 |
68
+ | 浏览器 fetch | `Mozilla/5.0 ...` | ✅ 200 |
69
+ | Python `urllib.request` | **`Python-urllib/3.x`** | ❌ 403 |
70
+
71
+ **修法**:
72
+ - Python `urllib`:手动加 `User-Agent: Mozilla/5.0`
73
+ - 或换 `requests` / `httpx` / Node / Go
74
+
75
+ ---
76
+
77
+ ## 🔴 `balance` 字段滞后
78
+
79
+ `/api/login` 和 `/api/me` 返回的 `balance` **不等于真实余额**(实测:`me` 返回 0,扣费 0.02 后真实余额 0.09,扣费仍成功)。
80
+
81
+ 可能解释:这两接口里 `balance` 字段没同步 D1,或来自一个非权威缓存层。
82
+
83
+ **操作规则**:
84
+ - 不要因为 `balance` 看起来够而**预先估算**余额
85
+ - 看到 `402` 不要立即判定没钱,先看 D1 控制台 https://promptfigure.top/console#account-balance
86
+ - 用户报告「明明有钱却被 402」时,先核对控制台余额(不是接口返回的)
87
+
88
+ ---
89
+
90
+ ## 注册 / key 相关
91
+
92
+ **Q:注册要收邮箱验证码吗?**
93
+ 不用。邮箱 + 密码(≥8 位)提交即完成,session token 立即可用。
94
+
95
+ **Q:key 明文丢了怎么办?**
96
+ 找不回来。库内只存 SHA-256 哈希。吊销旧的重建:
97
+ `POST /api/keys/revoke {"id":"..."}` → `POST /api/keys {"name":"..."}`。
98
+
99
+ **Q:余额和会员额度是一回事吗?**
100
+ 不是。API 只从**余额**扣,与订阅赠送的额度完全独立。有会员但余额为 0 → 调用仍会 402。反过来,只充值不订阅也能一直用 API(免费层 5 RPM)。
101
+
102
+ **Q:能创建几把 key?**
103
+ 每人最多 10 把未吊销。按用途命名便于追溯。
104
+
105
+ **Q:为什么会 401 但我明明没吊销?**
106
+ 先查 `Authorization` header —— 必须是 `Bearer pf_xxx`。写成 `Token pf_xxx` 或直接裸 key 都会 401。
107
+
108
+ ---
109
+
110
+ ## 参考图
111
+
112
+ **Q:`refIgnored: true` 是什么意思?**
113
+ 参考图缺失/非法/拉取失败,**已被忽略,当次照常出图并计费**。按序排查:
114
+
115
+ 1. URL 是图片**直链**吗?(浏览器打开直接显示图片,不是含图的网页)
116
+ 2. 是 PNG 吗?(某些 .webp/.jpg 后缀但实际格式不符会失败)
117
+ 3. 超过 8MB 吗?
118
+ 4. 是私网/本地地址吗?(会被拒绝)
119
+ 5. 图床是否还在?(免费图床政策常变)
120
+
121
+ **Q:参考图端点是不是有问题?**
122
+ **是**。上游 `/images/edits` 自 2026-09-07 起持续 503。`refUrl`/`refDataUrl` 当前经常 `refIgnored: true`。
123
+ **绕开方案**:暂时改用「纯文字精确描述」——把参考图的特征(方向/panel 数/图表类型/图标风格/数据标注密度/色分布)全部写进 prompt。修复后第一时间回写到本文件。
124
+
125
+ **Q:有既稳定又省事的方案吗?**
126
+ 图在公网 → 用 `refUrl`(服务器代取,你不用下载也不用转 base64)。
127
+ 图在本地且较小 → 用 `refDataUrl`。
128
+ 图在本地且较大 → 先挂免费图床:
129
+ `curl -F "file=@ref.png" https://x0.at`
130
+
131
+ ---
132
+
133
+ ## 出图质量
134
+
135
+ **Q:图里的文字糊/有乱码英文?**
136
+ polish:false 直出时图模型对英文文字标签渲染差(实测出 "Mcıuacy" 这种乱码)。
137
+ **修法**:prompt 里**逐字写对**所有英文标签,加一句 "All on-figure text labels in English, spelled correctly"。重要场合用 `premium`(gpt-image 文字渲染显著优于 Agnes standard)。
138
+
139
+ **Q:数值和我给的不一致?**
140
+ polish:false 时图模型会忠实画你给的数值,但也可能把"上下文中相似数字"画错。关键数值在 prompt 里重复一次并显式绑定单位。⚠️ 没有真实数据就别写——补出来的数值会被当成事实印到图上。
141
+
142
+ **Q:配色太单调,全是一个色系?**
143
+ prompt 里写了单一色相约束(如 `"muted steel-blue fills"`)。改用语义化多色(详见 `prompt-cookbook.md` 的配色小节)。
144
+
145
+ **Q:机制图被画成了 3D?**
146
+ prompt 里加 `2D flat`, `white background`, `no gradients, no photorealism`。
147
+
148
+ **Q:构图和我给的参考图差很远?**
149
+ 「参考风格」不拆特征是必返工的。拆到特征级——方向、panel 数、图表类型、图标风格、标注密度、色分布——逐条写进 prompt 正文。参考图只是补充(且当前参考图端点 503)。
150
+
151
+ **Q:prompt 很长但出图反而更差?**
152
+ 超 400 词后信息超载。压到 150–300 词,只保留核心实体与结构。
153
+
154
+ **Q:为什么图快/出图秒成但像样?**
155
+ polish:false 直出时快(~11s)但少了编排层润色,**质量低于正常管线是预期**,不是异常。正常管线应看到 `crafted: true` 的更慢但更好的结果。
156
+
157
+ ---
158
+
159
+ ## 计费
160
+
161
+ **Q:既然 premium 1K 和 2K 同价,为什么还要选 1K?**
162
+ 基本不用选,默认 2K。仅当需要快速迭代或尺寸受限时用 1K。
163
+
164
+ **Q:失败会扣钱吗?**
165
+ 不会。502、429 都会自动原路退款(响应里看 `refunded`)。只有成功出图才真扣费。
166
+
167
+ **Q:RPM 不够用怎么办?**
168
+ 提升订阅档(Lite 10 / Plus 15 / Pro 40 / Ultra 80 RPM)。短期也可以:错峰、把批量任务摊到不同分钟。
169
+
170
+ ---
171
+
172
+ ## 时效
173
+
174
+ **Q:响应要多久?**
175
+ polish:false 实测 ~11s(standard)/ 60–120s(premium 2K 偶尔更长)。**客户端 timeout ≥ 300s**。
176
+ ⚠️ 别设 30s/60s 就以为服务挂了——通常是你自己先断开了。
177
+
178
+ **Q:网页工作台和 API 一样慢/卡吗?**
179
+ 管线正常时网页与 API 同速(都在服务端润色)。网页端**没有 `polish:false` 等价开关**——上游限频期网页会卡在 polishing,急用请走 API 路径 A。
180
+
181
+ ---
182
+
183
+ ## 仍然解决不了
184
+
185
+ - 线上文档(权威,比本文件更新):https://promptfigure.top/docs/zh-CN/faq
186
+ - 在线调试台(隔离是代码问题还是账号问题):https://promptfigure.top/docs/zh-CN/api-playground
187
+ - 服务状态/更新:https://promptfigure.top/news
188
+
189
+ ---
190
+
191
+ ## premium 被内容审核误伤(502 `content moderation`,2026-09-10 CVPR 实测)
192
+
193
+ 现象:502 + `detail.error = premium channel rejected` + 上游报 "rejected by content moderation",**自动退款**。
194
+
195
+ 关键实测结论(二分验证):
196
+
197
+ - 触发是**整段组合判断,不是单词命中**——把整段 prompt 里的词逐个/分组喂给极简探针全部通过,合在一起就被拒。密集的「检测/候选框/过滤/一致性」类 CV 术语组合(如 …Candidate Boxes + Consensus + Filtering + 本地化器… 同屏多个)容易触发
198
+ - 单个可疑缩写也可能命中(实测 `WBF` 被拒,全称 Weighted Box Fusion 反而通过)——先用极简探针 + 可疑词单独测,拒=免费,通过=正常扣费,别拿整段 prompt 反复烧钱试
199
+
200
+ 处置顺序:
201
+
202
+ 1. 拒了就换措辞重试**最多 2 次**(每次拒绝免费);去掉标题/缩写、把检测类词汇换成中性词(Estimator/Candidate)常能过
203
+ 2. 还不过 → **不要继续试探**:改走 standard 出图(Agnes 通道审核宽松,同样内容能过),拼写/小字问题用 PIL 本地修补(采样盒底色覆盖 + Arial 按原字号重写标签),成本为零且拼写百分百正确
204
+ 3. 台账里记 `moderation_blocked: true` 与被拒 prompt,便于服务端侧后续排查
@@ -375,6 +375,37 @@ pf craft --at "§3.2 ¶2" --figure-type pipeline \
375
375
  - **路径写法**:Git Bash 的 `/c/...`、Windows 的 `C:\...`、POSIX 的 `/home/...` 都能用;
376
376
  但 craft --out 建议用相对路径(当前目录下),最不容易踩环境差异。
377
377
 
378
+ ## 每轮交付与台账(每轮结束必交,CLI 优先)
379
+
380
+ **CLI 优先**:出图、核验、审批、定稿全走 `pf` 命令(`pf next` 永远告诉你下一步);
381
+ GUI 只留给人两件事——亲眼看图、点审批。插件的门禁与留痕都挂在 CLI 上,
382
+ 走 CLI 的每一步在 `events.jsonl` 里可审计;无头环境(SSH/容器/CI)本来就全 CLI。
383
+
384
+ 每轮交付在**用户工作目录**建 `promptfigure-out/`,每轮一个新版本文件夹 `vN/`(v1 → v2…):
385
+
386
+ ```
387
+ promptfigure-out/
388
+ ├── vN/
389
+ │ ├── round-vN.md ← 本轮反馈(下面的必含项)
390
+ │ └── figures/ ← 本轮图(副本或引用,文件名带 -vN)
391
+ ├── paper-with-figures.md ← 论文+已插图的演进版(无关段落省略,每次更新同一个)
392
+ └── final/paper-final.md + 格式副本 ← 最终论文版本(定稿时才建)
393
+ ```
394
+
395
+ **round-vN.md 必含**(与 promptfigure-api skill 的 deliverables-ledger.md 同一协议):
396
+ 1. **本轮清单**:每张图——figureId / 图种 / 档位 / 版本 / 本机真实路径(本插件即
397
+ `~/.promptfigure/projects/<docId>/figures/<figureId>/vN.png`)/ 插入位置(§¶)
398
+ 2. **插入位置(两段边界)**:上一段最后一句 + 「……」 + 下一段第一句,用户一眼定位
399
+ 3. **所用上下文**:用了哪几段明确到 §¶;上下文只放**首句 + 「……」 + 尾句**;
400
+ 数据/表格/代码文件一律给引用链接,不内联内容
401
+ 4. **图片版本**:每张图的每个版本独立文件(vN),旧版永不覆盖
402
+ 5. **优化前→优化后**(优化任务):原图路径 vs 新图路径 + 改了什么 + 所用上下文(首尾句)
403
+
404
+ **最终成稿流程**(用户说「定稿」):提醒核对数据是否更新 → 用户确认后才 premium 定稿 →
405
+ 定稿后必问矢量图(本地 AI 重绘、token 贵、速度慢;比赛/数模默认不做;确认做则按元素
406
+ 分层重绘成可编辑矢量,PPT/WPS 按用户偏好)——完整协议见 promptfigure-api skill 的
407
+ `references/deliverables-ledger.md`。
408
+
378
409
  ## 事件回放
379
410
 
380
411
  `~/.promptfigure/projects/<docId>/events.jsonl` 只追加不改写 —— 审批历史、
package/web/app.js CHANGED
@@ -407,21 +407,27 @@ function paintFigures() {
407
407
  // ---------- 审批控件(侧栏卡片与浮层共用 —— 审批永远针对具体的图) ----------
408
408
  function reviewControls(f) {
409
409
  const rev = state.review[f.figureId] || { status: "pending", note: "" };
410
+ const byLabel = rev.by === "ai" ? '<span class="by-ai" title="这条判定是 AI 写的,不是你——请自己看一眼图再改判">⚠ AI 判定</span>'
411
+ : rev.by === "user" ? '<span class="by-user" title="你本人点的">本人</span>' : "";
410
412
  const box = document.createElement("div");
411
413
  box.className = "review-ctl";
412
414
  box.innerHTML = `
413
- <div class="meta">${badgeOf(rev.status)} <b>${escapeHtml(f.figureId)}</b> @${escapeHtml(f.at)} · ${escapeHtml(latestVer(f).model)}${f.versions.length > 1 ? ` · ${f.versions.length} 版` : ""}</div>
415
+ <div class="meta">${badgeOf(rev.status)} <b>${escapeHtml(f.figureId)}</b> @${escapeHtml(f.at)} · ${escapeHtml(latestVer(f).model)}${f.versions.length > 1 ? ` · ${f.versions.length} 版` : ""}${byLabel ? ` · 审批来源 ${byLabel}` : ""}</div>
414
416
  ${rev.note ? `<div class="meta">意见:${escapeHtml(rev.note)}</div>` : ""}
415
- <textarea class="note-box" placeholder="哪里不对?(可选,AI 会读到)"></textarea>
417
+ <textarea class="note-box" placeholder="写哪里不对或建议(AI 会读到;点「驳回」请写具体缺陷)"></textarea>
416
418
  <div class="btn-row">
417
419
  <button class="btn ok" data-act="approve">通过</button>
418
420
  <button class="btn no" data-act="reject">驳回</button>
419
421
  </div>
422
+ <div class="btn-hint">通过后 AI 才能出高清定稿 · 驳回 = AI 按意见重画 · 反馈 = 只提建议、图仍待你审批</div>
420
423
  <div class="btn-row"><button class="btn ghost" data-act="feedback">提交反馈给 AI</button></div>`;
421
424
  box.querySelectorAll("[data-act]").forEach((btn) => {
422
425
  btn.onclick = async () => {
423
426
  const note = box.querySelector(".note-box").value.trim();
424
427
  const act = btn.dataset.act;
428
+ // 驳回不带意见 = AI 只能盲改(精修状态机拿不到具体缺陷)——拦一道
429
+ if (act === "reject" && !note &&
430
+ !confirm("驳回但没写意见 —— AI 只能盲改。\n建议在上方写清楚哪里不对;确定要空意见驳回?")) return;
425
431
  const status = act === "approve" ? "approved" : act === "feedback" ? "pending" : "rejected";
426
432
  const resp = await fetch(`/api/review?doc=${DOC}`, {
427
433
  method: "POST",
@@ -430,7 +436,7 @@ function reviewControls(f) {
430
436
  });
431
437
  const data = await resp.json();
432
438
  if (!resp.ok) return toast("提交失败:" + (data.error || resp.status));
433
- toast(act === "approve" ? "已通过,AI 可以出定稿了" : act === "feedback" ? "反馈已提交,AI 会读到" : "已驳回");
439
+ toast(act === "approve" ? "已通过,AI 会接着出高清版" : act === "feedback" ? "反馈已提交,AI 会读到" : "已驳回,AI 会按你的意见重画");
434
440
  await loadState(); paintAll();
435
441
  // 审批队列开着 → 这张已离开队列,原位自动落到下一张
436
442
  if (!$("#queue").classList.contains("hidden")) renderQueue();
package/web/style.css CHANGED
@@ -136,6 +136,11 @@ main { flex: 1; display: flex; min-height: 0; }
136
136
  .btn.no { background: var(--red-line); color: #fff; }
137
137
  .btn.no:hover { filter: brightness(1.08); }
138
138
  .btn.ghost { background: #eef1f6; color: var(--ink); }
139
+ /* 审批按钮后果说明(三个按钮的区别一眼可见,别让用户猜) */
140
+ .btn-hint { font-size: 11px; color: var(--ink2); margin-top: 6px; line-height: 1.5; }
141
+ /* 审批来源:AI 自批的图必须让人一眼看出来(监督闭环) */
142
+ .by-ai { background: #fde8e8; color: #9b2c2c; border-radius: 8px; padding: 0 6px; font-weight: 600; }
143
+ .by-user { background: #e8f3ec; color: #1c6b3a; border-radius: 8px; padding: 0 6px; }
139
144
 
140
145
  .evt { font-size: 12px; padding: 6px 0; border-bottom: 1px dashed var(--line); color: var(--ink2); }
141
146
  .evt b { color: var(--ink); font-weight: 600; }