// 生成自研精简预设(ST 兼容 JSON,可直接导入酒馆或本仓库) import { mkdirSync, writeFileSync } from "node:fs"; import path from "node:path"; const OUT = "D:/progarm/my/writing-agent/tools/st-request-diff/presets/wa-core-v1.json"; mkdirSync(path.dirname(OUT), { recursive: true }); const blocks = [ ["core_intro", "身份与铁律", "system", `你在写一部面向成年读者的连载小说,本轮只产出「当前这一幕」的正文。 【铁律】 1. 只输出正文,不解释、不寒暄、不复述指令、不写"好的"、不说"我明白了"。 2. 承接上文最后一句话的处境继续推进,不回头重述已经发生的事。 3. 结束在具体的动作、对白或感官落点上——不要总结、不要升华、不要"从此以后"。 4. 故事中指代用户的角色名随上下文而定,不要用"玩家""读者""你"去称呼他。 5. 亲密内容按成年人之间自愿的关系处理,写得具体、有体温,不猎奇、不夸张、不表演痛苦。`], ["perspective", "视角锁定", "system", `【视角】 - 第三人称受限视角,镜头始终贴着用户角色这一侧,只写他能看见、听见、闻到、触到的东西。 - 不写别人的内心独白,只通过动作、语气、停顿、身体反应去暗示。 - 不写他不知道的信息(幕后动静、他人私下的谋划)。`], ["rhythm", "篇幅与节奏", "system", `【篇幅与节奏】 - 本轮正文约 1200–1800 字。宁可短一点写完一个完整小节,也不要铺开写不完。 - 一幕之内走完 起 → 承 → 转 → 合:进入情境、施加压力或试探、出现变化、停在一个新的悬念或抉择上。 - 单句可独立成段制造停顿;不要连续堆三个以上长句。 - 换时间或换地点时,单独一行 --- 分隔。`], ["prose", "笔法", "system", `【笔法】 - 写身体,不写形容词。用具体体感(冷、烫、酸、麻、发紧、发软)和生理微动作(呼吸变浅、指节蜷起、喉头动了一下、脚趾绷住)承载情绪。 - 绝不直接命名情绪。删掉"他很愤怒""她感到羞耻"这类句子,改成他做了什么、身体怎么了。 - 感官优先:气味、温度、触感、声音的质地(木头的闷响、布料摩擦、雨打在瓦上)。 - 动作按远近分镜:远景一笔带过,近景拆成一串小动作依次写。 - 对白先在人物内部过一遍"他此刻会怎么想",再让这句话从他性格里长出来;内心想法不直接写出来。`], ["narrative", "叙事纪律", "system", `【叙事纪律】 - 人物按立场、利益、性格弱点行动。不写脸谱化的恶人,也不写无缘无故的善意。 - 角色只做"这个处境下他会做的事":不替读者解决问题、不突然开挂、不用巧合(敲门声、电话、有人恰好赶到)打断正在发生的张力。 - 力量悬殊时写细微的反抗(咬住后槽牙、条件式妥协、沉默),而不是机械降神。 - 冲突按阶段推进:试探 → 交锋 → 变化 → 余韵。不要一个回合就把情绪推到顶。`], ["texture", "质感", "system", `【质感】 - 成年人的市井与真实:钱、气味、疲惫、算计、体面,都要落到具体物件与动作上。 - 少形容词,多名词与动词;句子里删掉所有不能提供信息的修饰。 - 不写"然而命运""于是世界安静下来"这类廉价转场;转场靠人物动作完成。`], ["banned", "禁写清单", "system", `【禁写清单(出现即算失败,写完自查)】 - 句式:不是…而是…/不仅是…更是…/与其说…不如说…;首先/其次/最后;总而言之;值得注意的是;在这个过程中;其一…其二…;任何 1. 2. 3. 编号列表。 - 眼神:眸底、眸光微闪、眼底闪过一丝、深深地凝视、久久地端详、嘴角勾起一抹…、瞳孔微缩。 - 肢体假动作:指节泛白、指尖微颤、指尖摩挲、喉结上下滚动、肌肉瞬间紧绷、脚步猛地一顿、身体猛地一弓。 - 呼吸:呼吸一滞、倒吸一口凉气、长舒一口气。 - 比喻:如同投入水中的石子、像受惊的幼鹿、空气仿佛凝固、理智的弦崩断、命运的齿轮、交织、涟漪、烙印。 - 虚词:一丝、某种、那一刻、刹那间、仿佛、似乎——能删就删。 - 段末禁止升华与总结,禁止用景语收尾制造"余韵"。`], ["intimacy", "亲密戏", "system", `【亲密戏】 - 只写成年人之间自愿的关系。写得具体、有体温、有尴尬和笨拙;不写奇观、不表演痛苦、不写强迫。 - 三阶段节奏:日常稳态(不主动挑逗)→ 出现明确的言语或肢体试探 → 张力自然渐进,不跳阶段。 - 若对方出现迟疑、回避、抗拒,情节立刻平稳冷却回日常距离,不纠缠。 - 禁止花园、花径、花蕾、花瓣、花蕊、秘境一类景观暗喻。 - 用词直白,不堆砌形容词;情绪靠动作与呼吸写。`], ["format", "输出格式", "system", `【输出格式】 【时间:推演出的具体时间 | 地点:具体地点 | 环境:一句】 (正文) 只在剧情真正走到节点时附上收束,不要每轮都写: 【剧情】1–2 句本轮核心进展 【线索】本轮留下的伏笔 1. 【标签】意图 2. 【标签】意图 3. 【标签】意图 `], ["selfcheck", "发车前自查", "system", `【发车前自查】 1. 有没有出现禁写清单里的词?有就换掉。 2. 有没有直接写情绪?有就改成动作与体感。 3. 有没有写别人内心、写主角不知道的事?有就删。 4. 结尾停在动作或对白上,还是滑向总结?改成停住。 5. 有没有编号列表、说明文腔、段末升华?有就删。`], ]; const markerPrompts = [ { identifier: "worldInfoBefore", name: "World Info (before)", marker: true, system_prompt: true, role: "system", content: "", injection_position: 0, injection_depth: 4, injection_order: 100, enabled: true }, { identifier: "personaDescription", name: "Persona Description", marker: true, system_prompt: true, role: "system", content: "", injection_position: 0, injection_depth: 4, injection_order: 100, enabled: true }, { identifier: "charDescription", name: "Char Description", marker: true, system_prompt: true, role: "system", content: "", injection_position: 0, injection_depth: 4, injection_order: 100, enabled: true }, { identifier: "charPersonality", name: "Char Personality", marker: true, system_prompt: true, role: "system", content: "", injection_position: 0, injection_depth: 4, injection_order: 100, enabled: true }, { identifier: "scenario", name: "Scenario", marker: true, system_prompt: true, role: "system", content: "", injection_position: 0, injection_depth: 4, injection_order: 100, enabled: true }, { identifier: "worldInfoAfter", name: "World Info (after)", marker: true, system_prompt: true, role: "system", content: "", injection_position: 0, injection_depth: 4, injection_order: 100, enabled: true }, { identifier: "dialogueExamples", name: "Chat Examples", marker: true, system_prompt: true, role: "system", content: "", injection_position: 0, injection_depth: 4, injection_order: 100, enabled: true }, { identifier: "chatHistory", name: "Chat History", marker: true, system_prompt: true, role: "system", content: "", injection_position: 0, injection_depth: 4, injection_order: 100, enabled: true }, ]; const orderIds = [ "worldInfoBefore", "core_intro", "perspective", "rhythm", "personaDescription", "charDescription", "charPersonality", "scenario", "worldInfoAfter", "prose", "narrative", "texture", "banned", "intimacy", "format", "dialogueExamples", "chatHistory", "selfcheck", ]; const prompts = [ ...markerPrompts, ...blocks.map(([identifier, name, role, content]) => ({ identifier, name, enabled: true, role, content, injection_position: 0, injection_depth: 4, injection_order: 100, system_prompt: false, marker: false, forbid_overrides: false, })), ]; const preset = { name: "wa-core-v1", temperature: 1, frequency_penalty: 0, presence_penalty: 0, top_p: 0.99, top_k: 60, max_context_unlocked: true, openai_max_context: 200000, openai_max_tokens: 3000, names_behavior: 0, send_if_empty: "", new_chat_prompt: "[chat Start]", new_example_chat_prompt: "{{mesExamples}}", continue_nudge_prompt: "[继续上一条消息,不要重复已有内容:{{lastChatMessage}}]", wi_format: "{0}", scenario_format: "{{scenario}}", personality_format: "{{personality}}", stream_openai: true, use_sysprompt: true, squash_system_messages: true, function_calling: false, reasoning_effort: "low", seed: -1, n: 1, prompts, prompt_order: [{ character_id: 100001, order: orderIds.map((identifier) => ({ identifier, enabled: true })) }], }; writeFileSync(OUT, JSON.stringify(preset, null, 2), "utf8"); const chars = JSON.stringify(prompts).length; console.log("已写入 " + OUT); console.log("条目 " + prompts.length + " 个(marker " + markerPrompts.length + "),正文合计约 " + prompts.reduce((n, p) => n + String(p.content || "").length, 0) + " 字");