promptfigure 0.2.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 +95 -0
- package/adapters/claude-code/SKILL.md +381 -0
- package/adapters/claude-code/install.mjs +9 -0
- package/adapters/codex/marketplace.json +14 -0
- package/adapters/codex/promptfigure/.codex-plugin/plugin.json +6 -0
- package/adapters/codex/promptfigure/skills/promptfigure-local/SKILL.md +381 -0
- package/bin/pf.mjs +1953 -0
- package/package.json +44 -0
- package/scripts/build-adapters.mjs +80 -0
- package/scripts/test-e2e.mjs +117 -0
- package/skill/promptfigure-local/SKILL.md +381 -0
- package/src/anchor.mjs +63 -0
- package/src/config.mjs +75 -0
- package/src/craft-rules.mjs +158 -0
- package/src/craft.mjs +600 -0
- package/src/doc/docx.mjs +69 -0
- package/src/doc/index.mjs +35 -0
- package/src/doc/para.mjs +54 -0
- package/src/doc/tex.mjs +161 -0
- package/src/doc/texbuild.mjs +158 -0
- package/src/docsearch.mjs +96 -0
- package/src/entity-pair.mjs +17 -0
- package/src/events.mjs +52 -0
- package/src/extract.mjs +124 -0
- package/src/figure-catalog.mjs +326 -0
- package/src/journal.mjs +67 -0
- package/src/ledger.mjs +43 -0
- package/src/next.mjs +125 -0
- package/src/plan.mjs +112 -0
- package/src/png-trim.mjs +217 -0
- package/src/quality.mjs +325 -0
- package/src/ratio.mjs +87 -0
- package/src/render.mjs +247 -0
- package/src/review.mjs +48 -0
- package/src/server.mjs +218 -0
- package/src/store.mjs +91 -0
- package/src/vectorize.mjs +54 -0
- package/tray/pf-tray.py +265 -0
- package/web/app.js +613 -0
- package/web/index.html +73 -0
- package/web/probe.html +36 -0
- package/web/style.css +208 -0
package/src/anchor.mjs
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// anchor.mjs — 锚点台账
|
|
2
|
+
// 锚点由 AI 显式声明(--at + --side),插件不猜。
|
|
3
|
+
// 🔴 相似度只当"要不要提醒 AI 看一眼"的粗筛(changed 状态),绝不参与定位决策
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
import { readAnchors, writeAnchors, readMeta } from "./store.mjs";
|
|
6
|
+
import { resolveRef, checkQuote } from "./doc/para.mjs";
|
|
7
|
+
|
|
8
|
+
export function setAnchor(docId, { at, side = "after", quote, figure }) {
|
|
9
|
+
const meta = readMeta(docId);
|
|
10
|
+
if (!meta) throw new Error(`项目 ${docId} 不存在`);
|
|
11
|
+
const target = resolveRef(meta.blocks, at); // 校验引用合法(抛错则命令失败)
|
|
12
|
+
const anchors = readAnchors(docId);
|
|
13
|
+
|
|
14
|
+
// 同一位置同一侧已有锚点 → 复用(AI 改图重出时不会堆锚点)
|
|
15
|
+
const exist = anchors.find((a) => a.placement.at === at && a.placement.side === side);
|
|
16
|
+
if (exist) {
|
|
17
|
+
if (figure) exist.figure = figure;
|
|
18
|
+
// 🔴 红队实测(2026-09-23 fuzzA):复用路径不更新快照——先 quote 编造句(changed)再
|
|
19
|
+
// quote 正确原文,回显 ✅ 但快照仍是旧错句,changed 永远无法自愈。
|
|
20
|
+
const q = quote ?? exist.placement.quote;
|
|
21
|
+
exist.state = checkQuote(meta.blocks, q);
|
|
22
|
+
if (quote && exist.state === "ok" && q !== exist.placement.quote) {
|
|
23
|
+
exist.placement.quote = q; // 新 quote 对上原文才换快照;对不上的旧快照保留作证据
|
|
24
|
+
}
|
|
25
|
+
exist.updatedAt = new Date().toISOString();
|
|
26
|
+
writeAnchors(docId, anchors);
|
|
27
|
+
return { anchor: exist, reused: true, quoteOk: exist.state === "ok" };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 🔴 2026-09-21 弱模型实测:新建锚点路径写死 state:"ok",编造的 quote 被静默收下
|
|
31
|
+
// (weak-agent-sim 第 14 轮:AI 编了句"YOLO-based end-to-end pipeline"当引文,直接 ✅)。
|
|
32
|
+
// quote 给了就必须当场对账原文;对不上 → state:"changed" + quoteOk:false,CLI 打警告。
|
|
33
|
+
const q = quote ?? target.text.slice(0, 160);
|
|
34
|
+
const state = checkQuote(meta.blocks, q);
|
|
35
|
+
const anchor = {
|
|
36
|
+
id: "a" + crypto.randomBytes(2).toString("hex"),
|
|
37
|
+
docId,
|
|
38
|
+
placement: {
|
|
39
|
+
at,
|
|
40
|
+
side, // before | after
|
|
41
|
+
quote: q, // 建锚时该段原文快照(变化检测用)
|
|
42
|
+
},
|
|
43
|
+
figure: figure || null,
|
|
44
|
+
state, // ok | changed(quote 对不上原文)| lost
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
};
|
|
47
|
+
anchors.push(anchor);
|
|
48
|
+
writeAnchors(docId, anchors);
|
|
49
|
+
return { anchor, reused: false, quoteOk: state === "ok" };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function listAnchors(docId, { changedOnly = false } = {}) {
|
|
53
|
+
const meta = readMeta(docId);
|
|
54
|
+
if (!meta) throw new Error(`项目 ${docId} 不存在`);
|
|
55
|
+
let anchors = readAnchors(docId);
|
|
56
|
+
// 状态每次现算:quote 是否还能在当前文档里找到
|
|
57
|
+
for (const a of anchors) {
|
|
58
|
+
a.state = meta.kind === "pdf" ? a.state : checkQuote(meta.blocks, a.placement.quote);
|
|
59
|
+
}
|
|
60
|
+
writeAnchors(docId, anchors);
|
|
61
|
+
if (changedOnly) anchors = anchors.filter((a) => a.state !== "ok");
|
|
62
|
+
return anchors;
|
|
63
|
+
}
|
package/src/config.mjs
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// config.mjs — ~/.promptfigure/config.json 读写
|
|
2
|
+
// 🔴 该文件含 pf_ key:权限 600;任何日志禁止打印 key(只打印前 8 位脱敏)
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
|
+
|
|
8
|
+
export const PF_DIR = path.join(os.homedir(), ".promptfigure");
|
|
9
|
+
export const CONFIG_PATH = path.join(PF_DIR, "config.json");
|
|
10
|
+
export const DAEMON_PATH = path.join(PF_DIR, "daemon.json");
|
|
11
|
+
export const PROJECTS_DIR = path.join(PF_DIR, "projects");
|
|
12
|
+
// 🔴 全链路唯一缺省端口(2026-09-21 实测):曾出现 pf.mjs/server.mjs 缺省 7420、tray 17420
|
|
13
|
+
// 的分裂——daemon.json 一被删两条链路各起各的,CLI 和托盘互相找不到。改端口只改这里。
|
|
14
|
+
// 用户环境千奇百怪:端口被占/被防火墙拦时,设 PF_PORT 环境变量即可整体换端口。
|
|
15
|
+
export const DEFAULT_PORT = Number(process.env.PF_PORT) || 17420;
|
|
16
|
+
|
|
17
|
+
// 🔴 无头环境判定(2026-09-21 用户定规:插件不许写死某台机器的环境):
|
|
18
|
+
// 用户的 AI 可能在 Codex Cloud/SSH 容器(无 GUI、无 pythonw、无 Edge/Chrome)里跑。
|
|
19
|
+
// - PF_HEADLESS 环境变量最高优先(=1 强制无头,=0 强制有头)
|
|
20
|
+
// - Windows 默认有头;macOS 默认有头(Aqua 不需要 DISPLAY);Linux 无 DISPLAY/WAYLAND = 无头
|
|
21
|
+
export function isHeadless() {
|
|
22
|
+
const forced = process.env.PF_HEADLESS;
|
|
23
|
+
if (forced !== undefined) return forced !== "0";
|
|
24
|
+
if (process.platform === "win32") return false;
|
|
25
|
+
if (process.platform === "darwin") return !!process.env.CI;
|
|
26
|
+
return !process.env.CI ? !(process.env.DISPLAY || process.env.WAYLAND_DISPLAY) : true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function ensureDirs() {
|
|
30
|
+
fs.mkdirSync(PROJECTS_DIR, { recursive: true });
|
|
31
|
+
fs.mkdirSync(path.join(PF_DIR, "logs"), { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function maskKey(key) {
|
|
35
|
+
if (!key) return "(未设置)";
|
|
36
|
+
return key.slice(0, 8) + "…";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function loadConfig() {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
|
|
42
|
+
} catch {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function saveConfig(patch) {
|
|
48
|
+
ensureDirs();
|
|
49
|
+
const cfg = { ...loadConfig(), ...patch };
|
|
50
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2));
|
|
51
|
+
try { fs.chmodSync(CONFIG_PATH, 0o600); } catch {} // Windows 上无效果,尽力而为
|
|
52
|
+
return cfg;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function docIdOf(absPath) {
|
|
56
|
+
return crypto.createHash("sha256").update(path.resolve(absPath)).digest("hex").slice(0, 12);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---- daemon.json:本地服务运行信息(唯一由 server.mjs 写入)----
|
|
60
|
+
export function readDaemon() {
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(fs.readFileSync(DAEMON_PATH, "utf8"));
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function writeDaemon(info) {
|
|
69
|
+
fs.writeFileSync(DAEMON_PATH, JSON.stringify(info, null, 2));
|
|
70
|
+
try { fs.chmodSync(DAEMON_PATH, 0o600); } catch {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function clearDaemon() {
|
|
74
|
+
try { fs.unlinkSync(DAEMON_PATH); } catch {}
|
|
75
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// craft-rules.mjs — 第二阶段「本地提示词规则层」纯数据模块
|
|
2
|
+
//
|
|
3
|
+
// 来源:服务端多年踩坑沉淀的确定性规则,从 EasyDraw-main 蒸馏而来。
|
|
4
|
+
// 所有正则/数据均带「源:」注释,便于日后跟服务端同步。
|
|
5
|
+
// · 自省句逻辑:functions/_shared/promptcraft.js stripSelfNarration L317-364
|
|
6
|
+
// · 输出硬约束 :functions/_shared/promptcraft.js OUTPUT_GUARD L157-163
|
|
7
|
+
// · 审查协议 :skills/promptcraft/REVIEW.md(A1-A5 / B1-B5)
|
|
8
|
+
// · 各 guard :functions/_shared/promptcraft.js applyDeterministicFixes L381-432
|
|
9
|
+
// · 通用工艺层 :functions/_shared/promptcraft.js UNIVERSAL_CRAFT L71-83
|
|
10
|
+
// · 负向清单 :functions/_shared/promptcraft.js COMMON_RULES Avoid L46
|
|
11
|
+
//
|
|
12
|
+
// 本模块零依赖、纯 ESM,Node 22 直接可 import。
|
|
13
|
+
|
|
14
|
+
// ---------- 自省句(模型对自己说的诚实性元评论)----------
|
|
15
|
+
// 源: promptcraft.js stripSelfNarration SELF_NARRATION_RE L332
|
|
16
|
+
// 蒸馏为多条「子句级」正则(sanitizePrompt 按子句切分后逐条匹配删除)。
|
|
17
|
+
// ⚠️ 边界:只删「元评论式」诚实性声明,绝不删视觉负向项(no watermark /
|
|
18
|
+
// no drop shadows / no decorative icons)——后者对图模型是有效约束。
|
|
19
|
+
export const SELF_NARRATION_PATTERNS = [
|
|
20
|
+
// 源: SELF_NARRATION_RE 分支 1 —— "no invented/fabricated/made-up numbers/data/..."
|
|
21
|
+
/(?:no|without|zero)\s+(?:invented|fabricated|made[- ]?up|fake|unsupported|hallucinated)\s+(?:\w+\s+){0,2}(?:numbers?|data|values?|statistics|figures?|metrics?|results?|counts?)/i,
|
|
22
|
+
// 源: SELF_NARRATION_RE 分支 2 —— "never/do not invent/fabricate/make up ..."
|
|
23
|
+
/(?:never|do\s+not|don't|avoid)\s+(?:inventing|invent|fabricating|fabricate|making\s+up|make\s+up)/i,
|
|
24
|
+
// 源: SELF_NARRATION_RE 分支 3 —— "no/specific numeric values/labels/annotations"
|
|
25
|
+
/(?:no|without)\s+(?:specific\s+)?numeric\s+(?:values?|labels?|annotations?)/i,
|
|
26
|
+
// 源: SELF_NARRATION_RE 分支 4 —— "values are/were omitted/withheld/not shown/..."
|
|
27
|
+
/values?\s+(?:are\s+|were\s+)?(?:omitted|withheld|not\s+(?:shown|specified|provided|invented|fabricated))/i,
|
|
28
|
+
// 源: SELF_NARRATION_RE 分支 5 —— "magnitude not specified/quantified"
|
|
29
|
+
/magnitude\s+not\s+(?:specified|quantified)/i,
|
|
30
|
+
// 源: SELF_NARRATION_RE 分支 6 —— "fabricated data/numbers/values/statistics"
|
|
31
|
+
/fabricated\s+(?:data|numbers?|values?|statistics)/i,
|
|
32
|
+
// 源: REVIEW.md A2 / OUTPUT_GUARD L158「复述约束本身」—— "no numeric values are printed" 类元评论
|
|
33
|
+
/no\s+numeric\s+(?:values?|labels?)\s+(?:are\s+|were\s+)?(?:printed|shown|included|present|used)/i,
|
|
34
|
+
// 源: OUTPUT_GUARD L158「不要宣布没有数字」—— "values omitted / not specified" 复述句
|
|
35
|
+
/(?:no|without)\s+(?:invented|fabricated)\s+(?:numeric\s+)?(?:values?|data)/i,
|
|
36
|
+
// 源: SELF_NARRATION_RE 分支 2 放宽 —— 覆盖 "I will not invent / will not invent / not invent" 变体
|
|
37
|
+
// (服务端正则要求 "do not/never/avoid" 紧贴 invent;裸 "will not invent" 同样属自省句,必须删)
|
|
38
|
+
/(?:I\s+will\s+not|will\s+not|do\s+not|don't|never|avoid)\s+(?:invent|fabricate|make\s+up|hallucinate|forge)/i,
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
// ---------- 违禁 / 平台样板模式(可确定性修复的部分)----------
|
|
42
|
+
// 每条描述一个确定性动作,由 craft.mjs 的 sanitizePrompt 解释执行。
|
|
43
|
+
// 源: promptcraft.js applyDeterministicFixes L381-432 各 guard。
|
|
44
|
+
export const BANNED_PATTERNS = [
|
|
45
|
+
{
|
|
46
|
+
id: "hex",
|
|
47
|
+
// 源: promptcraft.js humanizeHexPalette L439-465 + hexGuard L410-415
|
|
48
|
+
// 图模型对负向指令服从率低,唯一可靠手段是让 hex 码「不存在」。
|
|
49
|
+
action: "hexHumanize",
|
|
50
|
+
source: "promptcraft.js humanizeHexPalette L439-465 / hexGuard L410-415",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "cjk",
|
|
54
|
+
// 源: promptcraft.js stripStrayCJK L286-315(引号外 CJK 残留剔除)
|
|
55
|
+
// 仅删引号外的裸 CJK(翻译残留),引号内文本是图上标签原文,保留。
|
|
56
|
+
action: "stripStrayCJK",
|
|
57
|
+
source: "promptcraft.js stripStrayCJK L286-315",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// hex → 科学图常用色名标准池(用于确定性 humanize,最近邻映射)。
|
|
62
|
+
// 源: promptcraft.js HEX_NAMED_COLORS L439-449(RGB 值原样保留)。
|
|
63
|
+
export const HEX_NAMED_COLORS = [
|
|
64
|
+
["deep blue", 15, 77, 146], ["navy", 24, 38, 84], ["blue", 46, 116, 182],
|
|
65
|
+
["sky blue", 112, 179, 222], ["teal", 66, 148, 158], ["cyan", 88, 188, 218],
|
|
66
|
+
["green", 76, 155, 106], ["dark green", 44, 104, 72], ["pale green", 155, 205, 155],
|
|
67
|
+
["olive", 128, 132, 48], ["yellow", 230, 200, 60], ["amber", 222, 156, 32],
|
|
68
|
+
["orange", 212, 118, 58], ["coral", 233, 166, 161], ["red", 192, 57, 43],
|
|
69
|
+
["vermilion", 182, 67, 66], ["crimson", 153, 27, 45], ["magenta", 170, 62, 143],
|
|
70
|
+
["purple", 106, 76, 156], ["lavender", 180, 192, 228], ["brown", 121, 85, 61],
|
|
71
|
+
["pink", 230, 143, 172], ["grey", 118, 118, 118], ["dark grey", 72, 72, 78],
|
|
72
|
+
["light grey", 200, 200, 200], ["black", 26, 26, 26],
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
// ---------- 科研风格基线 ----------
|
|
76
|
+
// 源: promptcraft.js UNIVERSAL_CRAFT L71-83(Rendering discipline)+ COMMON_RULES L40-46
|
|
77
|
+
// + OUTPUT_GUARD L163「flat editorial style」。白底、无阴影无渐变、扁平色块、
|
|
78
|
+
// 克制色板 2-4 色、清晰字号层级。
|
|
79
|
+
export const STYLE_BASELINE = [
|
|
80
|
+
"White background, clean publication-grade canvas.",
|
|
81
|
+
"Flat editorial rendering: NO drop shadows, NO glow, NO gradient decoration unless explicitly requested; separate elements with crisp borders or whitespace.",
|
|
82
|
+
"Restrained semantic palette: 2-4 hues plus neutral greys/black/white; one accent colour on at most 1-2 focal elements; colourblind-safe pairing (never red-vs-green as the only distinction).",
|
|
83
|
+
"Exactly THREE visible text tiers — figure title/panel headers (largest), element/series labels (medium), annotations/axis numbers (smallest); keep total text items low (ideally under 15 short labels).",
|
|
84
|
+
"All lines and text crisp anti-aliased edges, print-quality, no sketch texture.",
|
|
85
|
+
"Keep at least a quarter of the canvas calm (whitespace is part of the design).",
|
|
86
|
+
].join(" ");
|
|
87
|
+
|
|
88
|
+
// ---------- 视觉负向项(图模型有效约束,非自省句)----------
|
|
89
|
+
// 源: promptcraft.js COMMON_RULES Avoid L46 + UNIVERSAL_CRAFT Rendering L83
|
|
90
|
+
// + TECH_RULES L174(no lens flare / no photorealistic branding)
|
|
91
|
+
export const NEGATIVE_LIST = [
|
|
92
|
+
"no watermark or logos",
|
|
93
|
+
"no drop shadows",
|
|
94
|
+
"no glow",
|
|
95
|
+
"no gradient decoration",
|
|
96
|
+
"no 3D chart junk",
|
|
97
|
+
"no photographic textures unless asked",
|
|
98
|
+
// 🔴 2026-09-21 premium v7 实测:图模型自加了清单外的 bullet 注释/箭头连接词/图例项,
|
|
99
|
+
// 且把"抽象白块"母题画成雪山照片。文字白名单与抽象母题约束(对图模型可执行的负向措辞):
|
|
100
|
+
"no text on the figure beyond the quoted stage titles and entity labels (no self-added bullets, arrow captions, legend entries or numbering; no full sentences printed inside cards — card text is short labels only)",
|
|
101
|
+
"abstract placeholder shapes stay abstract plain shapes (a 'white rounded rectangle' is NOT a photograph of a real scene)",
|
|
102
|
+
"no dense text blocks",
|
|
103
|
+
"no decorative clutter",
|
|
104
|
+
// ⚠️ "no invented data" 已删(weak-model-eval 2026-09-20):它是写手元评论而非视觉负向项,
|
|
105
|
+
// 会被自家 SELF_NARRATION_PATTERNS 误删(规则源冲突),对图模型也无画面意义。防编造归 writerGuard。
|
|
106
|
+
"no decorative pictograms or object icons (vegetable sketches, molecule doodads, product silhouettes) unless explicitly asked",
|
|
107
|
+
"no lens flare",
|
|
108
|
+
"no photorealistic branding",
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
// ---------- 版式预设(对标竞品"产物直接投稿可用")----------
|
|
112
|
+
// 依据:论文图有物理版式约束(双栏图横贯两栏、单栏图窄幅),竞品(AutoFigure /
|
|
113
|
+
// PaperBanana)把版式写进生成约束;图模型不守版式,用户就要手工裁剪重排。
|
|
114
|
+
export const PRESET_LAYOUTS = {
|
|
115
|
+
"double-column": "Composition sized for a TWO-COLUMN paper figure spanning full text width: wide landscape layout, left-to-right reading flow, main content in a horizontal band, no wasted side margins.",
|
|
116
|
+
"single-column": "Composition sized for a SINGLE-COLUMN paper figure: compact portrait or square layout, top-to-bottom reading flow, large enough fonts to stay legible at ~8 cm print width.",
|
|
117
|
+
"slide": "Composition sized for a presentation slide: 16:9 landscape, bolder line weights and larger labels than print figures, readable from the back of a room.",
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// ---------- 渲染后视觉核验清单(对标 ARS v3.3 VLM 图件核验 / AutoFigure Review-Refine)----------
|
|
121
|
+
// 🔴 与 craftPrompt 的 CHECKLIST(出图前、文本层)不同:这张清单是图渲染出来之后
|
|
122
|
+
// 宿主 AI 亲眼读图逐条核验用的。竞品用 VLM 评委 + 重画闭环;本地版把"评委"交给宿主
|
|
123
|
+
// AI 的视觉能力(零成本),清单必须可判定(看一眼有答案),不打分。
|
|
124
|
+
export const QA_CHECKLIST = [
|
|
125
|
+
"Q1 文字卫生 — 图内没有乱码/伪文字(gibberish glyphs)、没有拼错的英文单词;带文字的图必须 premium 出稿(standard 渲文字必乱码)。",
|
|
126
|
+
"Q2 实体逐字 — 🔴 先把图上全部可见文字逐条列出(一张都不许漏),再逐条对照白名单(阶段标题+实体清单):多一条、改一字、多一句都算失败;卡面出现完整说明句(任何语言:明显长于标签、带句读或含助词/动词句尾,且不是实体/标题)= 直接 FAIL(V11 事故:说明句被印进卡面,抽样式核验漏检)。",
|
|
127
|
+
"Q3 结构保真 — 箭头方向/流程顺序/包含关系与声明的结构一致;机制图的因果方向错 = 直接重画( confidently wrong 比 omit 更糟)。",
|
|
128
|
+
"Q4 无编造 — 图上没有清单之外的新模块、新曲线、新数值、新图例项;数出来的元素个数与实体数一致。",
|
|
129
|
+
"Q5 版式与底色 — 布局只有一种阅读朝向;版式预设(双栏/单栏)符合;留白占画布 1/10~1/3:少于 1/10 拥挤,🔴 多于 1/3 = 内容带没撑满画布 = FAIL(V11 事故:上下空白带 >40%,排进双栏后文字会被压得极小);🔴 背景必须纯白 —— 重点检查四角与空白区域的淡淡斑块/纸张纹理/有机斑点底纹,不是刺眼的白 = 不过(standard 档高发,实测出现过且极易漏检)。",
|
|
130
|
+
"Q6 配色与装饰 — 色板 ≤ 4 色相 + 中性色;无红绿唯一区分;hex 码没有作为文字印在图上;无阴影/渐变/3D 装饰;🔴 无装饰性图形符号(鸟兽/人物/器物小剪影点缀)——除非实体清单明确要求(standard 档高发,实测出现过)。",
|
|
131
|
+
"Q7 风格一致 — 若本文档设了风格卡:这张图与同批其他图放在一起色板/线条/字体观感一致(全批对照,不只看单张)。",
|
|
132
|
+
"Q8 原文溯源 + 原图对照(重绘任务必过)— 双重验收:①原文溯源:图中每个阶段/实体/数据流步骤都能在论文正文(图 ref 所在 section 及其方法章节)找到出处(§¶);正文没提的模块或母题不许画,正文明确写的步骤不许漏、顺序不许错。②原图对照:原图的每个卡片/面板在新图中有对应物,标签逐字一致。没读过相关正文与原图 PNG 就渲染 = 流程违规,先回退补读。",
|
|
133
|
+
"Q9 画法落实 — 逐阶段对照任务包第 3c 步清单:每个阶段盒子存在、标题逐字一致;[画法] 条目描述的视觉元素(缩略图阵列/合并箭头/门形分组/热图小块…)真的以**图形**画出,而不是被文字化或直接省略;[标签] 条目以 ≤5 词文字印在盒内。漏画 = 布局漂移 FAIL,缺陷写具体到阶段(如「阶段2缺 IoU 合并视觉」),精修状态机按阶段定位重拼。",
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
// ---------- 输出硬约束(紧贴 user 末尾的硬约束行)----------
|
|
137
|
+
// 源: promptcraft.js OUTPUT_GUARD L157-163 六条蒸馏。
|
|
138
|
+
// ⚠️ 不给偷懒出口(OUTPUT_GUARD 注释 L149-156 踩坑):
|
|
139
|
+
// 禁止 "or use neutral placeholders" 这类措辞(3.0 会改成占位符敷衍量级);
|
|
140
|
+
// 禁止模糊量词("roughly half")当范例;
|
|
141
|
+
// 禁止复述约束本身("no numeric values" 写成产出一部分 = 元评论)。
|
|
142
|
+
export const OUTPUT_GUARD_LINES = [
|
|
143
|
+
// 源: OUTPUT_GUARD 第 1 条 L158
|
|
144
|
+
"Numbers: use ONLY numbers the user supplied. Never invent counts, sizes, resolutions, percentages, performance figures, symbols or formulas. When the user asks for a magnitude they never quantified, encode it VISUALLY — draw the block noticeably larger or smaller, the bar taller or shorter, the panel wider — or distinguish with neutral tags (Variant A, Variant B). Never announce the absence of numbers and never leave an empty placeholder (Value, Comparable).",
|
|
145
|
+
// 源: OUTPUT_GUARD 第 2 条 L159
|
|
146
|
+
"Structure: never invent components, activation states, ablation settings, mechanisms, symbols or names the user did not give. Where a needed detail is unspecified, use a neutral placeholder (Variant A, Path 1, Module 1) — never a realistic-sounding name or a made-up technical detail.",
|
|
147
|
+
// 源: OUTPUT_GUARD 第 3 条 L160
|
|
148
|
+
"Coverage: silently check off EVERY explicit request in the user's text — each entity, number, unit, label wording, arrow or data-flow requirement, comparison, annotation request and emphasis — and make sure every one appears in the output. Dropping a requested item is worse than an imperfect layout.",
|
|
149
|
+
// 源: OUTPUT_GUARD 第 4 条 L161
|
|
150
|
+
"Verbatim: keep every user-supplied entity, number, unit, dose, condition and label wording exactly as given.",
|
|
151
|
+
// 源: OUTPUT_GUARD 第 5 条 L162
|
|
152
|
+
"Format: emit the field headers in order — Style anchor, Overall layout, Content blocks, Palette, Emphasis, Scale & text space, Avoid — nothing before the first header and nothing after the last.",
|
|
153
|
+
// 源: OUTPUT_GUARD 第 6 条 L163
|
|
154
|
+
"Output only the prompt itself: no commentary, no notes about these constraints, no questions.",
|
|
155
|
+
// 源: promptcraft.js hexGuard L410-415 + labelPurityGuard L427-430(确定性追加守卫,本地一并钉死)
|
|
156
|
+
"Apply colours directly to the shapes they belong to — never print hex codes or colour values as visible text on the figure, and do not draw a colour legend that names the colours.",
|
|
157
|
+
"Render every quoted label exactly once, exactly as quoted — do not add English translations or synonyms alongside them, never duplicate a label, and do not prefix labels with list markers the label text does not include.",
|
|
158
|
+
];
|