refactor: extract execution prompt assembly
This commit is contained in:
138
tests/execution-memory-resolver.test.ts
Normal file
138
tests/execution-memory-resolver.test.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import {
|
||||
resolveRelevantMemoriesForTesting,
|
||||
resolveRuntimeRelevantMemoriesForTesting,
|
||||
} from "@/lib/execution/memory-resolver";
|
||||
|
||||
test("MemoryResolver 在 master-agent 会话下优先挑当前请求命中的项目记忆", () => {
|
||||
const resolved = resolveRelevantMemoriesForTesting({
|
||||
projectId: "master-agent",
|
||||
requestText: "boss-console 的审批流",
|
||||
memories: [
|
||||
{
|
||||
memoryId: "m1",
|
||||
scope: "project",
|
||||
projectId: "boss-console",
|
||||
title: "审批流",
|
||||
content: "boss-console approval",
|
||||
tags: ["approval"],
|
||||
memoryType: "project_progress",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
updatedAt: "2026-01-01T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
memoryId: "m2",
|
||||
scope: "project",
|
||||
projectId: "wenshenapp",
|
||||
title: "UI",
|
||||
content: "wechat ui",
|
||||
tags: ["ui"],
|
||||
memoryType: "project_progress",
|
||||
createdAt: "2026-01-02T00:00:00.000Z",
|
||||
updatedAt: "2026-01-02T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.equal(resolved.projectMemories.length, 1);
|
||||
assert.equal(resolved.projectMemories[0]?.projectId, "boss-console");
|
||||
});
|
||||
|
||||
test("MemoryResolver 会保留全局记忆的输入顺序并只截断到 8 条", () => {
|
||||
const resolved = resolveRelevantMemoriesForTesting({
|
||||
projectId: "master-agent",
|
||||
memories: [
|
||||
{
|
||||
memoryId: "g1",
|
||||
scope: "global",
|
||||
title: "一",
|
||||
content: "one",
|
||||
tags: [],
|
||||
memoryType: "decision",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
updatedAt: "2026-01-01T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
memoryId: "g2",
|
||||
scope: "global",
|
||||
title: "二",
|
||||
content: "two",
|
||||
tags: [],
|
||||
memoryType: "decision",
|
||||
createdAt: "2026-01-02T00:00:00.000Z",
|
||||
updatedAt: "2026-01-02T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
resolved.userMemories.map((memory) => memory.memoryId),
|
||||
["g1", "g2"],
|
||||
);
|
||||
});
|
||||
|
||||
test("Runtime MemoryResolver 会优先排布 workflow_rule 和 user_preference 全局记忆", () => {
|
||||
const resolved = resolveRuntimeRelevantMemoriesForTesting({
|
||||
projectId: "master-agent",
|
||||
memories: [
|
||||
{
|
||||
memoryId: "g1",
|
||||
scope: "global",
|
||||
title: "普通记忆",
|
||||
content: "normal",
|
||||
tags: [],
|
||||
memoryType: "decision",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
updatedAt: "2026-01-01T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
memoryId: "g2",
|
||||
scope: "global",
|
||||
title: "规则记忆",
|
||||
content: "workflow",
|
||||
tags: [],
|
||||
memoryType: "workflow_rule",
|
||||
createdAt: "2026-01-03T00:00:00.000Z",
|
||||
updatedAt: "2026-01-03T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
memoryId: "g3",
|
||||
scope: "global",
|
||||
title: "偏好记忆",
|
||||
content: "preference",
|
||||
tags: [],
|
||||
memoryType: "user_preference",
|
||||
createdAt: "2026-01-02T00:00:00.000Z",
|
||||
updatedAt: "2026-01-02T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
resolved.userMemories.map((memory) => memory.memoryId),
|
||||
["g2", "g3", "g1"],
|
||||
);
|
||||
});
|
||||
|
||||
test("Runtime MemoryResolver 在 master-agent 非空请求但无 lexical 命中时回退到前 6 个项目记忆", () => {
|
||||
const resolved = resolveRuntimeRelevantMemoriesForTesting({
|
||||
projectId: "master-agent",
|
||||
requestText: "xyzxyz no overlap",
|
||||
memories: [
|
||||
{ memoryId: "p1", scope: "project", projectId: "boss-console", title: "alpha", content: "alpha-content", tags: [], memoryType: "project_progress", createdAt: "2026-01-01T00:00:00.000Z", updatedAt: "2026-01-01T00:00:00.000Z" },
|
||||
{ memoryId: "p2", scope: "project", projectId: "boss-console", title: "bravo", content: "bravo-content", tags: [], memoryType: "project_progress", createdAt: "2026-01-02T00:00:00.000Z", updatedAt: "2026-01-02T00:00:00.000Z" },
|
||||
{ memoryId: "p3", scope: "project", projectId: "boss-console", title: "charlie", content: "charlie-content", tags: [], memoryType: "project_progress", createdAt: "2026-01-03T00:00:00.000Z", updatedAt: "2026-01-03T00:00:00.000Z" },
|
||||
{ memoryId: "p4", scope: "project", projectId: "boss-console", title: "delta", content: "delta-content", tags: [], memoryType: "project_progress", createdAt: "2026-01-04T00:00:00.000Z", updatedAt: "2026-01-04T00:00:00.000Z" },
|
||||
{ memoryId: "p5", scope: "project", projectId: "boss-console", title: "echo", content: "echo-content", tags: [], memoryType: "project_progress", createdAt: "2026-01-05T00:00:00.000Z", updatedAt: "2026-01-05T00:00:00.000Z" },
|
||||
{ memoryId: "p6", scope: "project", projectId: "boss-console", title: "foxtrot", content: "foxtrot-content", tags: [], memoryType: "project_progress", createdAt: "2026-01-06T00:00:00.000Z", updatedAt: "2026-01-06T00:00:00.000Z" },
|
||||
{ memoryId: "p7", scope: "project", projectId: "boss-console", title: "golf", content: "golf-content", tags: [], memoryType: "project_progress", createdAt: "2026-01-07T00:00:00.000Z", updatedAt: "2026-01-07T00:00:00.000Z" },
|
||||
],
|
||||
});
|
||||
|
||||
assert.equal(resolved.projectMemories.length, 6);
|
||||
assert.deepEqual(
|
||||
resolved.projectMemories.map((memory) => memory.memoryId),
|
||||
["p1", "p2", "p3", "p4", "p5", "p6"],
|
||||
);
|
||||
});
|
||||
63
tests/execution-prompt-assembler.test.ts
Normal file
63
tests/execution-prompt-assembler.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import {
|
||||
buildExecutionPromptForTesting,
|
||||
resolveRelevantMemoriesForTesting,
|
||||
} from "@/lib/execution/prompt-assembler";
|
||||
|
||||
test("PromptAssembler 会按固定顺序拼管理员提示词、用户提示词、对话提示词和记忆", () => {
|
||||
const prompt = buildExecutionPromptForTesting({
|
||||
globalPrompt: "GLOBAL",
|
||||
userPrompt: "USER",
|
||||
conversationPrompt: "CONVERSATION",
|
||||
projectMemories: [{ title: "项目记忆", content: "PROJECT", projectId: "boss-console", tags: [] }],
|
||||
userMemories: [{ title: "用户记忆", content: "USER_MEMORY", tags: [] }],
|
||||
requestText: "继续",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
prompt,
|
||||
[
|
||||
"管理员全局主提示词:\nGLOBAL",
|
||||
"用户私有主提示词:\nUSER",
|
||||
"当前对话附加提示词:\nCONVERSATION",
|
||||
"项目记忆:\n- [boss-console] 项目记忆: PROJECT",
|
||||
"用户通用记忆:\n- 用户记忆: USER_MEMORY",
|
||||
"当前消息:\n继续",
|
||||
].join("\n\n"),
|
||||
);
|
||||
});
|
||||
|
||||
test("PromptAssembler 会透出可测试的记忆筛选器", () => {
|
||||
const resolved = resolveRelevantMemoriesForTesting({
|
||||
projectId: "master-agent",
|
||||
requestText: "boss-console 的审批流",
|
||||
memories: [
|
||||
{
|
||||
memoryId: "m1",
|
||||
scope: "project",
|
||||
projectId: "boss-console",
|
||||
title: "审批流",
|
||||
content: "boss-console approval",
|
||||
tags: ["approval"],
|
||||
memoryType: "project_progress",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
updatedAt: "2026-01-01T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
memoryId: "m2",
|
||||
scope: "project",
|
||||
projectId: "wenshenapp",
|
||||
title: "UI",
|
||||
content: "wechat ui",
|
||||
tags: ["ui"],
|
||||
memoryType: "project_progress",
|
||||
createdAt: "2026-01-02T00:00:00.000Z",
|
||||
updatedAt: "2026-01-02T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.equal(resolved.projectMemories.length, 1);
|
||||
assert.equal(resolved.projectMemories[0]?.projectId, "boss-console");
|
||||
});
|
||||
@@ -11,6 +11,8 @@ let updateMasterAgentPromptPolicy: (typeof import("../src/lib/boss-data"))["upda
|
||||
let updateUserMasterPrompt: (typeof import("../src/lib/boss-data"))["updateUserMasterPrompt"];
|
||||
let createUserMasterMemory: (typeof import("../src/lib/boss-data"))["createUserMasterMemory"];
|
||||
let resolveMasterAgentExecutionConfig: (typeof import("../src/lib/boss-master-agent"))["resolveMasterAgentExecutionConfig"];
|
||||
let stateFile = "";
|
||||
let stateBackupFile = "";
|
||||
|
||||
async function setup() {
|
||||
if (runtimeRoot) return;
|
||||
@@ -18,6 +20,8 @@ async function setup() {
|
||||
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-config-"));
|
||||
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
|
||||
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
|
||||
stateFile = process.env.BOSS_STATE_FILE;
|
||||
stateBackupFile = `${stateFile}.bak`;
|
||||
|
||||
const [data, masterAgent] = await Promise.all([
|
||||
import("../src/lib/boss-data.ts"),
|
||||
@@ -32,6 +36,21 @@ async function setup() {
|
||||
resolveMasterAgentExecutionConfig = masterAgent.resolveMasterAgentExecutionConfig;
|
||||
}
|
||||
|
||||
async function resetState() {
|
||||
if (!stateFile) {
|
||||
return;
|
||||
}
|
||||
await Promise.all([
|
||||
rm(stateFile, { force: true }),
|
||||
rm(stateBackupFile, { force: true }),
|
||||
]);
|
||||
}
|
||||
|
||||
test.beforeEach(async () => {
|
||||
await setup();
|
||||
await resetState();
|
||||
});
|
||||
|
||||
test.after(async () => {
|
||||
if (runtimeRoot) {
|
||||
await rm(runtimeRoot, { recursive: true, force: true });
|
||||
@@ -39,8 +58,6 @@ test.after(async () => {
|
||||
});
|
||||
|
||||
test("当前对话 override 优先于主控账号默认值", async () => {
|
||||
await setup();
|
||||
|
||||
await saveAiAccount({
|
||||
accountId: "master-codex-primary",
|
||||
label: "主 GPT",
|
||||
@@ -71,8 +88,6 @@ test("当前对话 override 优先于主控账号默认值", async () => {
|
||||
});
|
||||
|
||||
test("主 Agent 执行配置会合成管理员提示词、用户提示词和当前对话提示词", async () => {
|
||||
await setup();
|
||||
|
||||
await saveAiAccount({
|
||||
accountId: "master-codex-primary",
|
||||
label: "主 GPT",
|
||||
@@ -107,8 +122,6 @@ test("主 Agent 执行配置会合成管理员提示词、用户提示词和当
|
||||
});
|
||||
|
||||
test("主 Agent 执行 prompt 会明确声明管理员全局提示词不可覆盖,并带出项目记忆来源", async () => {
|
||||
await setup();
|
||||
|
||||
await saveAiAccount({
|
||||
accountId: "master-codex-primary",
|
||||
label: "主 GPT",
|
||||
@@ -128,6 +141,9 @@ test("主 Agent 执行 prompt 会明确声明管理员全局提示词不可覆
|
||||
updatedBy: "17600003315",
|
||||
});
|
||||
await updateUserMasterPrompt("17600003315", "用户私有主提示词");
|
||||
await updateProjectAgentControls("master-agent", {
|
||||
promptOverride: "当前对话提示词",
|
||||
});
|
||||
await createUserMasterMemory({
|
||||
account: "17600003315",
|
||||
scope: "project",
|
||||
@@ -153,12 +169,14 @@ test("主 Agent 执行 prompt 会明确声明管理员全局提示词不可覆
|
||||
"继续推进 boss 项目的会话归档逻辑",
|
||||
);
|
||||
|
||||
assert.match(
|
||||
assert.equal(
|
||||
resolved.executionPrompt,
|
||||
/管理员全局主提示词.*不可被.*覆盖|不可覆盖管理员全局主提示词/,
|
||||
);
|
||||
assert.match(
|
||||
resolved.executionPrompt,
|
||||
/projectId=boss-console/,
|
||||
[
|
||||
"管理员全局主提示词:\n系统级主提示词",
|
||||
"用户私有主提示词:\n用户私有主提示词",
|
||||
"当前对话附加提示词:\n当前对话提示词",
|
||||
"项目记忆:\n- [boss-console] boss 项目进度: boss 项目当前按项目聚合加线程下钻展示。",
|
||||
"当前消息:\n继续推进 boss 项目的会话归档逻辑",
|
||||
].join("\n\n"),
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user