mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-13 18:31:16 +08:00
feat(extract): add split extraction prompt plumbing
This commit is contained in:
@@ -37,6 +37,12 @@ const INPUT_CONTEXT_MVU_FIELDS = [
|
|||||||
"contradictionSummary",
|
"contradictionSummary",
|
||||||
"charDescription",
|
"charDescription",
|
||||||
"userPersona",
|
"userPersona",
|
||||||
|
"objectiveExtractionDraft",
|
||||||
|
"objectiveRefMap",
|
||||||
|
"ownerContext",
|
||||||
|
"batchStoryTime",
|
||||||
|
"relevantPovMemories",
|
||||||
|
"cognitionStateDigest",
|
||||||
];
|
];
|
||||||
|
|
||||||
const INPUT_REGEX_STAGE_BY_FIELD = {
|
const INPUT_REGEX_STAGE_BY_FIELD = {
|
||||||
@@ -51,6 +57,12 @@ const INPUT_REGEX_STAGE_BY_FIELD = {
|
|||||||
characterSummary: "input.candidateText",
|
characterSummary: "input.candidateText",
|
||||||
threadSummary: "input.candidateText",
|
threadSummary: "input.candidateText",
|
||||||
contradictionSummary: "input.candidateText",
|
contradictionSummary: "input.candidateText",
|
||||||
|
objectiveExtractionDraft: "input.candidateText",
|
||||||
|
objectiveRefMap: "input.candidateText",
|
||||||
|
ownerContext: "input.candidateText",
|
||||||
|
batchStoryTime: "input.candidateText",
|
||||||
|
relevantPovMemories: "input.candidateText",
|
||||||
|
cognitionStateDigest: "input.candidateText",
|
||||||
};
|
};
|
||||||
|
|
||||||
const INPUT_REGEX_ROLE_BY_FIELD = {
|
const INPUT_REGEX_ROLE_BY_FIELD = {
|
||||||
@@ -74,6 +86,12 @@ const INPUT_HOST_REGEX_SOURCE_BY_FIELD = {
|
|||||||
contradictionSummary: "ai_output",
|
contradictionSummary: "ai_output",
|
||||||
charDescription: "ai_output",
|
charDescription: "ai_output",
|
||||||
userPersona: "user_input",
|
userPersona: "user_input",
|
||||||
|
objectiveExtractionDraft: "ai_output",
|
||||||
|
objectiveRefMap: "ai_output",
|
||||||
|
ownerContext: "ai_output",
|
||||||
|
batchStoryTime: "ai_output",
|
||||||
|
relevantPovMemories: "ai_output",
|
||||||
|
cognitionStateDigest: "ai_output",
|
||||||
};
|
};
|
||||||
|
|
||||||
function cloneRuntimeDebugValue(value, fallback = null) {
|
function cloneRuntimeDebugValue(value, fallback = null) {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import { DEFAULT_TASK_PROFILE_TEMPLATES } from "./default-task-profile-templates
|
|||||||
|
|
||||||
const TASK_TYPES = [
|
const TASK_TYPES = [
|
||||||
"extract",
|
"extract",
|
||||||
|
"extract_objective",
|
||||||
|
"extract_subjective",
|
||||||
"recall",
|
"recall",
|
||||||
"compress",
|
"compress",
|
||||||
"synopsis",
|
"synopsis",
|
||||||
@@ -29,6 +31,16 @@ const TASK_TYPE_META = {
|
|||||||
label: "提取",
|
label: "提取",
|
||||||
description: "从当前对话批次中抽取结构化记忆。",
|
description: "从当前对话批次中抽取结构化记忆。",
|
||||||
},
|
},
|
||||||
|
extract_objective: {
|
||||||
|
label: "客观提取",
|
||||||
|
description: "从当前对话批次中抽取客观层结构化记忆。",
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
extract_subjective: {
|
||||||
|
label: "主观提取",
|
||||||
|
description: "从客观提取草稿与视角上下文中抽取主观记忆。",
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
recall: {
|
recall: {
|
||||||
label: "召回",
|
label: "召回",
|
||||||
description: "根据上下文筛选最相关的记忆节点。",
|
description: "根据上下文筛选最相关的记忆节点。",
|
||||||
@@ -186,6 +198,48 @@ const BUILTIN_BLOCK_DEFINITIONS = [
|
|||||||
role: "system",
|
role: "system",
|
||||||
description: "注入当前活跃的故事时间线标签与来源。extract 任务使用,帮助 LLM 定位本批对话在剧情时间轴上的位置。",
|
description: "注入当前活跃的故事时间线标签与来源。extract 任务使用,帮助 LLM 定位本批对话在剧情时间轴上的位置。",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
sourceKey: "objectiveExtractionDraft",
|
||||||
|
name: "客观提取草稿",
|
||||||
|
role: "system",
|
||||||
|
description: "注入未来拆分提取链路中的客观层提取草稿。仅供客观/主观拆分提取预设显式添加时使用。",
|
||||||
|
taskTypes: ["extract_objective", "extract_subjective"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceKey: "objectiveRefMap",
|
||||||
|
name: "客观引用映射",
|
||||||
|
role: "system",
|
||||||
|
description: "注入未来拆分提取链路中的客观层 ref 到节点/草稿的映射。仅供客观/主观拆分提取预设显式添加时使用。",
|
||||||
|
taskTypes: ["extract_objective", "extract_subjective"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceKey: "ownerContext",
|
||||||
|
name: "视角主体上下文",
|
||||||
|
role: "system",
|
||||||
|
description: "注入未来主观提取链路中的 POV owner 身份、作用域和相关约束。仅供拆分提取预设显式添加时使用。",
|
||||||
|
taskTypes: ["extract_objective", "extract_subjective"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceKey: "batchStoryTime",
|
||||||
|
name: "批次故事时间",
|
||||||
|
role: "system",
|
||||||
|
description: "注入未来拆分提取链路中的批次故事时间对象。仅供拆分提取预设显式添加时使用。",
|
||||||
|
taskTypes: ["extract_objective", "extract_subjective"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceKey: "relevantPovMemories",
|
||||||
|
name: "相关主观记忆",
|
||||||
|
role: "system",
|
||||||
|
description: "注入未来主观提取链路中与当前 owner 相关的既有 POV 记忆。仅供拆分提取预设显式添加时使用。",
|
||||||
|
taskTypes: ["extract_objective", "extract_subjective"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceKey: "cognitionStateDigest",
|
||||||
|
name: "认知状态摘要",
|
||||||
|
role: "system",
|
||||||
|
description: "注入未来主观提取链路中 owner 的认知状态摘要。仅供拆分提取预设显式添加时使用。",
|
||||||
|
taskTypes: ["extract_objective", "extract_subjective"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
sourceKey: "plannerCharacterCard",
|
sourceKey: "plannerCharacterCard",
|
||||||
name: "规划:角色卡",
|
name: "规划:角色卡",
|
||||||
@@ -239,6 +293,8 @@ const DEFAULT_TASK_INPUT = Object.freeze({
|
|||||||
|
|
||||||
const LEGACY_PROMPT_FIELD_MAP = {
|
const LEGACY_PROMPT_FIELD_MAP = {
|
||||||
extract: "extractPrompt",
|
extract: "extractPrompt",
|
||||||
|
extract_objective: "extractObjectivePrompt",
|
||||||
|
extract_subjective: "extractSubjectivePrompt",
|
||||||
recall: "recallPrompt",
|
recall: "recallPrompt",
|
||||||
compress: "compressPrompt",
|
compress: "compressPrompt",
|
||||||
synopsis: "synopsisPrompt",
|
synopsis: "synopsisPrompt",
|
||||||
@@ -1001,11 +1057,14 @@ function getDefaultTaskProfileTemplate(taskType) {
|
|||||||
if (String(taskType || "") === "planner") {
|
if (String(taskType || "") === "planner") {
|
||||||
return buildPlannerDefaultTaskProfileTemplate();
|
return buildPlannerDefaultTaskProfileTemplate();
|
||||||
}
|
}
|
||||||
const template = DEFAULT_TASK_PROFILE_TEMPLATES?.[taskType];
|
const templateKey = ["extract_objective", "extract_subjective"].includes(String(taskType || ""))
|
||||||
|
? "extract"
|
||||||
|
: taskType;
|
||||||
|
const template = DEFAULT_TASK_PROFILE_TEMPLATES?.[templateKey];
|
||||||
if (!template || typeof template !== "object") {
|
if (!template || typeof template !== "object") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return applyRuntimeDefaultTemplateOverrides(taskType, cloneJson(template));
|
return applyRuntimeDefaultTemplateOverrides(templateKey, cloneJson(template));
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashTemplateFingerprint(value = "") {
|
function hashTemplateFingerprint(value = "") {
|
||||||
@@ -2512,11 +2571,14 @@ export function getTaskTypeMeta(taskType) {
|
|||||||
id: taskType,
|
id: taskType,
|
||||||
label: TASK_TYPE_META[taskType]?.label || taskType,
|
label: TASK_TYPE_META[taskType]?.label || taskType,
|
||||||
description: TASK_TYPE_META[taskType]?.description || "",
|
description: TASK_TYPE_META[taskType]?.description || "",
|
||||||
|
hidden: TASK_TYPE_META[taskType]?.hidden === true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTaskTypeOptions() {
|
export function getTaskTypeOptions() {
|
||||||
return TASK_TYPES.map((taskType) => getTaskTypeMeta(taskType));
|
return TASK_TYPES
|
||||||
|
.map((taskType) => getTaskTypeMeta(taskType))
|
||||||
|
.filter((meta) => meta.hidden !== true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTaskTypes() {
|
export function getTaskTypes() {
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ export const defaultSettings = {
|
|||||||
|
|
||||||
// 自定义提示词
|
// 自定义提示词
|
||||||
extractPrompt: "",
|
extractPrompt: "",
|
||||||
|
extractObjectivePrompt: "",
|
||||||
|
extractSubjectivePrompt: "",
|
||||||
recallPrompt: "",
|
recallPrompt: "",
|
||||||
consolidationPrompt: "",
|
consolidationPrompt: "",
|
||||||
compressPrompt: "",
|
compressPrompt: "",
|
||||||
|
|||||||
@@ -110,8 +110,12 @@ assert.equal(defaultSettings.nativeRolloutVersion, 2);
|
|||||||
assert.equal(defaultSettings.nativeEngineFailOpen, true);
|
assert.equal(defaultSettings.nativeEngineFailOpen, true);
|
||||||
assert.equal(defaultSettings.graphNativeForceDisable, false);
|
assert.equal(defaultSettings.graphNativeForceDisable, false);
|
||||||
assert.equal(defaultSettings.taskProfilesVersion, 3);
|
assert.equal(defaultSettings.taskProfilesVersion, 3);
|
||||||
|
assert.equal(defaultSettings.extractObjectivePrompt, "");
|
||||||
|
assert.equal(defaultSettings.extractSubjectivePrompt, "");
|
||||||
assert.ok(defaultSettings.taskProfiles);
|
assert.ok(defaultSettings.taskProfiles);
|
||||||
assert.ok(defaultSettings.taskProfiles.extract);
|
assert.ok(defaultSettings.taskProfiles.extract);
|
||||||
|
assert.ok(defaultSettings.taskProfiles.extract_objective);
|
||||||
|
assert.ok(defaultSettings.taskProfiles.extract_subjective);
|
||||||
assert.ok(defaultSettings.taskProfiles.recall);
|
assert.ok(defaultSettings.taskProfiles.recall);
|
||||||
assert.ok(defaultSettings.globalTaskRegex);
|
assert.ok(defaultSettings.globalTaskRegex);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ installResolveHooks([
|
|||||||
|
|
||||||
const { buildTaskLlmPayload, buildTaskPrompt } = await import("../prompting/prompt-builder.js");
|
const { buildTaskLlmPayload, buildTaskPrompt } = await import("../prompting/prompt-builder.js");
|
||||||
const {
|
const {
|
||||||
|
createBuiltinPromptBlock,
|
||||||
createDefaultGlobalTaskRegex,
|
createDefaultGlobalTaskRegex,
|
||||||
createDefaultTaskProfiles,
|
createDefaultTaskProfiles,
|
||||||
} = await import("../prompting/prompt-profiles.js");
|
} = await import("../prompting/prompt-profiles.js");
|
||||||
@@ -256,4 +257,98 @@ assert.equal(
|
|||||||
|
|
||||||
initializeHostAdapter({});
|
initializeHostAdapter({});
|
||||||
|
|
||||||
|
const splitContextTaskProfiles = createDefaultTaskProfiles();
|
||||||
|
const subjectiveProfile = splitContextTaskProfiles.extract_subjective.profiles[0];
|
||||||
|
subjectiveProfile.blocks = [
|
||||||
|
createBuiltinPromptBlock("extract_subjective", "objectiveExtractionDraft", {
|
||||||
|
name: "客观提取草稿",
|
||||||
|
order: 0,
|
||||||
|
}),
|
||||||
|
createBuiltinPromptBlock("extract_subjective", "objectiveRefMap", {
|
||||||
|
name: "客观引用映射",
|
||||||
|
order: 1,
|
||||||
|
}),
|
||||||
|
createBuiltinPromptBlock("extract_subjective", "ownerContext", {
|
||||||
|
name: "视角主体上下文",
|
||||||
|
order: 2,
|
||||||
|
}),
|
||||||
|
createBuiltinPromptBlock("extract_subjective", "batchStoryTime", {
|
||||||
|
name: "批次故事时间",
|
||||||
|
order: 3,
|
||||||
|
}),
|
||||||
|
createBuiltinPromptBlock("extract_subjective", "relevantPovMemories", {
|
||||||
|
name: "相关主观记忆",
|
||||||
|
order: 4,
|
||||||
|
}),
|
||||||
|
createBuiltinPromptBlock("extract_subjective", "cognitionStateDigest", {
|
||||||
|
name: "认知状态摘要",
|
||||||
|
order: 5,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const splitContextPromptBuild = await buildTaskPrompt(
|
||||||
|
{
|
||||||
|
taskProfilesVersion: 3,
|
||||||
|
taskProfiles: splitContextTaskProfiles,
|
||||||
|
},
|
||||||
|
"extract_subjective",
|
||||||
|
{
|
||||||
|
objectiveExtractionDraft: { operations: [{ ref: "evt1", type: "event" }] },
|
||||||
|
objectiveRefMap: { evt1: "node-evt1" },
|
||||||
|
ownerContext: { ownerType: "character", ownerName: "艾琳" },
|
||||||
|
batchStoryTime: { label: "第二天清晨", confidence: "high" },
|
||||||
|
relevantPovMemories: ["旧 POV 记忆"],
|
||||||
|
cognitionStateDigest: "艾琳知道 evt1",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const splitContextPayload = buildTaskLlmPayload(
|
||||||
|
splitContextPromptBuild,
|
||||||
|
"fallback-user",
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
splitContextPayload.promptMessages
|
||||||
|
.map((message) => message.sourceKey)
|
||||||
|
.filter(Boolean),
|
||||||
|
[
|
||||||
|
"objectiveExtractionDraft",
|
||||||
|
"objectiveRefMap",
|
||||||
|
"ownerContext",
|
||||||
|
"batchStoryTime",
|
||||||
|
"relevantPovMemories",
|
||||||
|
"cognitionStateDigest",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
String(
|
||||||
|
splitContextPayload.promptMessages.find(
|
||||||
|
(message) => message.sourceKey === "objectiveExtractionDraft",
|
||||||
|
)?.content || "",
|
||||||
|
),
|
||||||
|
/"ref": "evt1"/,
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
String(
|
||||||
|
splitContextPayload.promptMessages.find(
|
||||||
|
(message) => message.sourceKey === "ownerContext",
|
||||||
|
)?.content || "",
|
||||||
|
),
|
||||||
|
/"ownerName": "艾琳"/,
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
String(
|
||||||
|
splitContextPayload.promptMessages.find(
|
||||||
|
(message) => message.sourceKey === "batchStoryTime",
|
||||||
|
)?.content || "",
|
||||||
|
),
|
||||||
|
/"第二天清晨"/,
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
String(
|
||||||
|
splitContextPayload.promptMessages.find(
|
||||||
|
(message) => message.sourceKey === "relevantPovMemories",
|
||||||
|
)?.content || "",
|
||||||
|
),
|
||||||
|
/旧 POV 记忆/,
|
||||||
|
);
|
||||||
|
|
||||||
console.log("prompt-builder-defaults tests passed");
|
console.log("prompt-builder-defaults tests passed");
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import {
|
|||||||
createLocalRegexRule,
|
createLocalRegexRule,
|
||||||
exportTaskProfile,
|
exportTaskProfile,
|
||||||
getActiveTaskProfile,
|
getActiveTaskProfile,
|
||||||
|
getBuiltinBlockDefinitions,
|
||||||
getLegacyPromptFieldForTask,
|
getLegacyPromptFieldForTask,
|
||||||
|
getTaskTypeMeta,
|
||||||
|
getTaskTypeOptions,
|
||||||
|
getTaskTypes,
|
||||||
importTaskProfile,
|
importTaskProfile,
|
||||||
restoreDefaultTaskProfile,
|
restoreDefaultTaskProfile,
|
||||||
upsertTaskProfile,
|
upsertTaskProfile,
|
||||||
@@ -97,4 +101,75 @@ const restoredActive = getActiveTaskProfile(
|
|||||||
assert.equal(restoredActive.id, "default");
|
assert.equal(restoredActive.id, "default");
|
||||||
assert.equal(getLegacyPromptFieldForTask("extract"), "extractPrompt");
|
assert.equal(getLegacyPromptFieldForTask("extract"), "extractPrompt");
|
||||||
|
|
||||||
|
assert.ok(getTaskTypes().includes("extract_objective"));
|
||||||
|
assert.ok(getTaskTypes().includes("extract_subjective"));
|
||||||
|
assert.equal(
|
||||||
|
getTaskTypeOptions().some((option) => option.id === "extract_objective"),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
getTaskTypeOptions().some((option) => option.id === "extract_subjective"),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
{
|
||||||
|
objective: getTaskTypeMeta("extract_objective"),
|
||||||
|
subjective: getTaskTypeMeta("extract_subjective"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
objective: {
|
||||||
|
id: "extract_objective",
|
||||||
|
label: "客观提取",
|
||||||
|
description: "从当前对话批次中抽取客观层结构化记忆。",
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
subjective: {
|
||||||
|
id: "extract_subjective",
|
||||||
|
label: "主观提取",
|
||||||
|
description: "从客观提取草稿与视角上下文中抽取主观记忆。",
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert.ok(taskProfiles.extract_objective?.profiles?.length > 0);
|
||||||
|
assert.ok(taskProfiles.extract_subjective?.profiles?.length > 0);
|
||||||
|
assert.equal(
|
||||||
|
taskProfiles.extract_objective.profiles[0].metadata.legacyPromptField,
|
||||||
|
"extractObjectivePrompt",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
taskProfiles.extract_subjective.profiles[0].metadata.legacyPromptField,
|
||||||
|
"extractSubjectivePrompt",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
taskProfiles.extract_objective.profiles[0].blocks.find((block) => block.id === "default-role")?.content,
|
||||||
|
baseProfile.blocks.find((block) => block.id === "default-role")?.content,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
taskProfiles.extract_subjective.profiles[0].blocks.find((block) => block.id === "default-rules")?.content,
|
||||||
|
baseProfile.blocks.find((block) => block.id === "default-rules")?.content,
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
getBuiltinBlockDefinitions("extract_subjective")
|
||||||
|
.map((definition) => definition.sourceKey)
|
||||||
|
.filter((sourceKey) =>
|
||||||
|
[
|
||||||
|
"objectiveExtractionDraft",
|
||||||
|
"objectiveRefMap",
|
||||||
|
"ownerContext",
|
||||||
|
"batchStoryTime",
|
||||||
|
"relevantPovMemories",
|
||||||
|
"cognitionStateDigest",
|
||||||
|
].includes(sourceKey),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
"objectiveExtractionDraft",
|
||||||
|
"objectiveRefMap",
|
||||||
|
"ownerContext",
|
||||||
|
"batchStoryTime",
|
||||||
|
"relevantPovMemories",
|
||||||
|
"cognitionStateDigest",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
console.log("task-profile-storage tests passed");
|
console.log("task-profile-storage tests passed");
|
||||||
|
|||||||
Reference in New Issue
Block a user