统一在预设包装层做提示词夹心,并修正探针实际发信路径。

让所有模型请求与界面预览共用同一套夹心,避免探针与运行时行为分叉。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-10 02:18:00 +08:00
co-authored by Cursor
parent beb4c332e6
commit d65ebbce8b
7 changed files with 252 additions and 32 deletions
+99 -5
View File
@@ -4,12 +4,32 @@ import { assemblePresetMessages } from "./assembler.js";
import { applyExtendedMarkers } from "./markers.js";
import {
resolveWorldInfoMarker,
type WorldInfoEntry,
type WorldInfoPack,
} from "./world-info-pack.js";
const TASK_SYSTEM_ENTRY_ID = "task-system";
const TASK_SYSTEM_ENTRY_NAME = "任务契约";
function withTaskSystem(
pack: WorldInfoPack,
systemPrompt: string,
): WorldInfoPack {
const text = systemPrompt.trim();
if (!text) return pack;
const entry: WorldInfoEntry = {
id: TASK_SYSTEM_ENTRY_ID,
name: TASK_SYSTEM_ENTRY_NAME,
content: text,
};
return {
...pack,
worldBookBefore: [entry, ...pack.worldBookBefore],
};
}
/**
* Play worker:system 契约 + 按扩展 prompt_order 填洞。
* 不要再把同一份 context-order 正文叠成第二包 user。
* 严格按扩展 prompt_order 填洞。任务契约进 worldBookBefore,不在预设外另挂 system。
*/
export function assemblePlayWorkerMessages(params: {
systemPrompt: string;
@@ -17,8 +37,82 @@ export function assemblePlayWorkerMessages(params: {
pack: WorldInfoPack;
}): ChatMessage[] {
const framed = applyExtendedMarkers(params.preset);
const filled = assemblePresetMessages(framed, (id) =>
resolveWorldInfoMarker(params.pack, id),
const pack = withTaskSystem(params.pack, params.systemPrompt);
return assemblePresetMessages(framed, (id) =>
resolveWorldInfoMarker(pack, id),
);
return [{ role: "system", content: params.systemPrompt }, ...filled];
}
function isToolLoopBoundary(m: ChatMessage): boolean {
if (m.role === "tool") return true;
return Boolean(
m.role === "assistant" && m.tool_calls && m.tool_calls.length > 0,
);
}
function formatHistoryMessages(msgs: ChatMessage[]): string {
const parts: string[] = [];
for (const m of msgs) {
if (m.role === "user" && m.content.trim()) {
parts.push(`【用户】\n${m.content.trim()}`);
} else if (m.role === "assistant") {
const text = (m.content ?? "").trim();
if (text) parts.push(`【助手】\n${text}`);
} else if (m.role === "system" && m.content.trim()) {
parts.push(`【系统】\n${m.content.trim()}`);
}
}
return parts.join("\n\n");
}
/**
* 任务 messages → 只填四袋 + chatHistory;发出序列的首尾由预设 prompt_order 决定。
* tool 循环尾接在夹心后。
*/
export function frameMessagesWithPreset(
preset: PresetPackage,
messages: ChatMessage[],
): ChatMessage[] {
let splitAt = messages.length;
for (let i = 0; i < messages.length; i++) {
if (isToolLoopBoundary(messages[i]!)) {
splitAt = i;
break;
}
}
const prefix = messages.slice(0, splitAt);
const continuation = messages.slice(splitAt);
const systems: string[] = [];
const body: ChatMessage[] = [];
for (const m of prefix) {
if (m.role === "system") systems.push(m.content);
else body.push(m);
}
let turn = "";
let history = "";
if (body.length > 0) {
const last = body[body.length - 1]!;
if (last.role === "user") {
turn = last.content.trim();
history = formatHistoryMessages(body.slice(0, -1));
} else {
history = formatHistoryMessages(body);
}
}
const framed = assemblePlayWorkerMessages({
systemPrompt: systems.join("\n\n"),
preset,
pack: {
worldBookBefore: [],
history,
worldBookAfter: [],
turn,
postTurn: [],
},
});
return continuation.length > 0 ? [...framed, ...continuation] : framed;
}