feat: add master-agent prompts and memory management

This commit is contained in:
kris
2026-04-01 04:10:11 +08:00
parent 9000a9f185
commit d316f0490e
31 changed files with 4398 additions and 32 deletions

View File

@@ -7,6 +7,8 @@ import { mkdtemp, rm } from "node:fs/promises";
let runtimeRoot = "";
let saveAiAccount: (typeof import("../src/lib/boss-data"))["saveAiAccount"];
let updateProjectAgentControls: (typeof import("../src/lib/boss-data"))["updateProjectAgentControls"];
let updateMasterAgentPromptPolicy: (typeof import("../src/lib/boss-data"))["updateMasterAgentPromptPolicy"];
let updateUserMasterPrompt: (typeof import("../src/lib/boss-data"))["updateUserMasterPrompt"];
let resolveMasterAgentExecutionConfig: (typeof import("../src/lib/boss-master-agent"))["resolveMasterAgentExecutionConfig"];
async function setup() {
@@ -23,6 +25,8 @@ async function setup() {
saveAiAccount = data.saveAiAccount;
updateProjectAgentControls = data.updateProjectAgentControls;
updateMasterAgentPromptPolicy = data.updateMasterAgentPromptPolicy;
updateUserMasterPrompt = data.updateUserMasterPrompt;
resolveMasterAgentExecutionConfig = masterAgent.resolveMasterAgentExecutionConfig;
}
@@ -63,3 +67,39 @@ test("当前对话 override 优先于主控账号默认值", async () => {
assert.equal(resolved.account.accountId, "master-codex-primary");
assert.equal(resolved.account.model, "gpt-4.1-mini");
});
test("主 Agent 执行配置会合成管理员提示词、用户提示词和当前对话提示词", async () => {
await setup();
await saveAiAccount({
accountId: "master-codex-primary",
label: "主 GPT",
role: "primary",
provider: "master_codex_node",
displayName: "Mac 上的 Master Codex Node",
nodeId: "local-codex-node",
nodeLabel: "本机 Codex",
model: "gpt-4.1-mini",
enabled: true,
setActive: true,
loginStatusNote: "通过绑定的 Master Codex Node 对话。",
});
await updateMasterAgentPromptPolicy({
globalPrompt: "全局主提示词",
updatedBy: "17600003315",
});
await updateUserMasterPrompt("17600003315", "用户私有主提示词");
await updateProjectAgentControls("master-agent", {
modelOverride: "gpt-5.4",
reasoningEffortOverride: "high",
promptOverride: "当前对话提示词",
});
const resolved = await resolveMasterAgentExecutionConfig("master-agent");
assert.equal(resolved.promptPolicy?.globalPrompt, "全局主提示词");
assert.equal(resolved.userPrompt?.content, "用户私有主提示词");
assert.equal(resolved.projectPromptOverride, "当前对话提示词");
assert.equal(resolved.promptPolicy?.updatedBy, "17600003315");
});

View File

@@ -0,0 +1,59 @@
import test from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import path from "node:path";
import { mkdtemp, rm } from "node:fs/promises";
let runtimeRoot = "";
let queueMasterAgentTask: (typeof import("../src/lib/boss-data"))["queueMasterAgentTask"];
let completeMasterAgentTask: (typeof import("../src/lib/boss-data"))["completeMasterAgentTask"];
let listUserMasterMemories: (typeof import("../src/lib/boss-data"))["listUserMasterMemories"];
async function setup() {
if (runtimeRoot) return;
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-memory-ingestion-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const data = await import("../src/lib/boss-data.ts");
queueMasterAgentTask = data.queueMasterAgentTask;
completeMasterAgentTask = data.completeMasterAgentTask;
listUserMasterMemories = data.listUserMasterMemories;
}
test.after(async () => {
if (runtimeRoot) {
await rm(runtimeRoot, { recursive: true, force: true });
}
});
test("主 Agent 完成对话后会自动沉淀用户偏好和项目记忆", async () => {
await setup();
const task = await queueMasterAgentTask({
projectId: "master-agent",
requestMessageId: "msg-user-1",
requestText: "boss 项目后续都按微信式交互来做,并且默认中文回复。",
executionPrompt: "prompt",
requestedBy: "17600003315",
requestedByAccount: "17600003315",
deviceId: "master-agent-openai",
});
await completeMasterAgentTask({
taskId: task.taskId,
deviceId: "master-agent-openai",
status: "completed",
replyBody: "boss 项目当前进度已更新:会话页会继续按微信式交互推进。",
});
const memories = await listUserMasterMemories("17600003315", { includeArchived: false });
const globalMemory = memories.find((memory) => memory.scope === "global");
const projectMemory = memories.find((memory) => memory.scope === "project" && memory.projectId === "boss-console");
assert.ok(globalMemory, "expected a global user memory");
assert.ok(projectMemory, "expected a project-scoped memory");
assert.match(globalMemory?.content ?? "", /微信式交互|中文回复/);
assert.match(projectMemory?.content ?? "", /boss 项目当前进度已更新/);
});

View File

@@ -0,0 +1,131 @@
import test from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import path from "node:path";
import { mkdtemp, rm } from "node:fs/promises";
import { NextRequest } from "next/server";
let runtimeRoot = "";
let AUTH_SESSION_COOKIE = "";
let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"];
let getMasterAgentPromptPolicyRoute: typeof import("../src/app/api/v1/master-agent/prompt-policy/route");
let getUserMasterPromptRoute: typeof import("../src/app/api/v1/master-agent/prompt/route");
let getUserMasterMemoriesRoute: typeof import("../src/app/api/v1/master-agent/memories/route");
let patchUserMasterMemoryRoute: typeof import("../src/app/api/v1/master-agent/memories/[memoryId]/route");
async function setup() {
if (runtimeRoot) return;
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-prompts-memory-routes-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const [data, auth, promptPolicyRoute, userPromptRoute, memoriesRoute, memoryRoute] = await Promise.all([
import("../src/lib/boss-data.ts"),
import("../src/lib/boss-auth.ts"),
import("../src/app/api/v1/master-agent/prompt-policy/route.ts"),
import("../src/app/api/v1/master-agent/prompt/route.ts"),
import("../src/app/api/v1/master-agent/memories/route.ts"),
import("../src/app/api/v1/master-agent/memories/[memoryId]/route.ts"),
]);
createAuthSession = data.createAuthSession;
AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE;
getMasterAgentPromptPolicyRoute = promptPolicyRoute;
getUserMasterPromptRoute = userPromptRoute;
getUserMasterMemoriesRoute = memoriesRoute;
patchUserMasterMemoryRoute = memoryRoute;
}
async function createAuthedRequest(account = "17600003315", role: "member" | "admin" | "highest_admin" = "highest_admin") {
await setup();
const session = await createAuthSession({
account,
role,
displayName: "Boss 超级管理员",
loginMethod: "password",
});
return {
headers: {
"content-type": "application/json",
cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`,
},
};
}
test.after(async () => {
if (runtimeRoot) {
await rm(runtimeRoot, { recursive: true, force: true });
}
});
test("master-agent prompt and memory routes support admin prompt, user prompt, and memory CRUD", async () => {
await setup();
const adminRequest = await createAuthedRequest();
const promptGet = await getMasterAgentPromptPolicyRoute.GET(
new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/prompt-policy", {
method: "GET",
headers: adminRequest.headers,
}),
);
assert.equal(promptGet.status, 200);
const promptPost = await getMasterAgentPromptPolicyRoute.POST(
new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/prompt-policy", {
method: "POST",
headers: adminRequest.headers,
body: JSON.stringify({ globalPrompt: "管理员全局主提示词" }),
}),
);
assert.equal(promptPost.status, 200);
const userPromptPost = await getUserMasterPromptRoute.POST(
new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/prompt", {
method: "POST",
headers: adminRequest.headers,
body: JSON.stringify({ content: "用户私有主提示词" }),
}),
);
assert.equal(userPromptPost.status, 200);
const memoriesPost = await getUserMasterMemoriesRoute.POST(
new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/memories", {
method: "POST",
headers: adminRequest.headers,
body: JSON.stringify({
scope: "project",
projectId: "master-agent",
title: "项目进度",
content: "主 Agent 提示词与记忆链路完成。",
memoryType: "project_progress",
tags: ["主Agent", "记忆"],
}),
}),
);
assert.equal(memoriesPost.status, 200);
const memoriesPayload = (await memoriesPost.json()) as {
ok: boolean;
memory?: { memoryId: string };
};
assert.equal(memoriesPayload.ok, true);
assert.ok(memoriesPayload.memory?.memoryId);
const patchResponse = await patchUserMasterMemoryRoute.PATCH(
new NextRequest(
`http://127.0.0.1:3000/api/v1/master-agent/memories/${memoriesPayload.memory?.memoryId}`,
{
method: "PATCH",
headers: adminRequest.headers,
body: JSON.stringify({
content: "主 Agent 提示词与记忆链路已完成。",
tags: ["提示词", "记忆"],
}),
},
),
{ params: Promise.resolve({ memoryId: memoriesPayload.memory?.memoryId ?? "" }) },
);
assert.equal(patchResponse.status, 200);
});

View File

@@ -0,0 +1,87 @@
import test from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import path from "node:path";
import { mkdtemp, rm } from "node:fs/promises";
let runtimeRoot = "";
let readState: (typeof import("../src/lib/boss-data"))["readState"];
let getMasterAgentPromptPolicy: (typeof import("../src/lib/boss-data"))["getMasterAgentPromptPolicy"];
let updateMasterAgentPromptPolicy: (typeof import("../src/lib/boss-data"))["updateMasterAgentPromptPolicy"];
let getUserMasterPrompt: (typeof import("../src/lib/boss-data"))["getUserMasterPrompt"];
let updateUserMasterPrompt: (typeof import("../src/lib/boss-data"))["updateUserMasterPrompt"];
let listUserMasterMemories: (typeof import("../src/lib/boss-data"))["listUserMasterMemories"];
let createUserMasterMemory: (typeof import("../src/lib/boss-data"))["createUserMasterMemory"];
let updateUserMasterMemory: (typeof import("../src/lib/boss-data"))["updateUserMasterMemory"];
let archiveUserMasterMemory: (typeof import("../src/lib/boss-data"))["archiveUserMasterMemory"];
async function setup() {
if (runtimeRoot) return;
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-prompts-memory-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const data = await import("../src/lib/boss-data.ts");
readState = data.readState;
getMasterAgentPromptPolicy = data.getMasterAgentPromptPolicy;
updateMasterAgentPromptPolicy = data.updateMasterAgentPromptPolicy;
getUserMasterPrompt = data.getUserMasterPrompt;
updateUserMasterPrompt = data.updateUserMasterPrompt;
listUserMasterMemories = data.listUserMasterMemories;
createUserMasterMemory = data.createUserMasterMemory;
updateUserMasterMemory = data.updateUserMasterMemory;
archiveUserMasterMemory = data.archiveUserMasterMemory;
}
test.after(async () => {
if (runtimeRoot) {
await rm(runtimeRoot, { recursive: true, force: true });
}
});
test("主 Agent 提示词与用户记忆可读写", async () => {
await setup();
await updateMasterAgentPromptPolicy({
globalPrompt: "全局主提示词",
updatedBy: "17600003315",
});
await updateUserMasterPrompt("17600003315", "用户私有主提示词");
const created = await createUserMasterMemory({
account: "17600003315",
scope: "project",
projectId: "master-agent",
title: "项目进度",
content: "当前主链优先打通聊天闭环。",
memoryType: "project_progress",
tags: ["聊天", "主链"],
});
await updateUserMasterMemory(created.memoryId, "17600003315", {
content: "当前主链优先打通主 Agent 聊天闭环。",
tags: ["聊天", "主Agent"],
});
const policy = await getMasterAgentPromptPolicy();
const userPrompt = await getUserMasterPrompt("17600003315");
const memories = await listUserMasterMemories("17600003315", {
includeArchived: false,
});
assert.equal(policy?.globalPrompt, "全局主提示词");
assert.equal(userPrompt?.content, "用户私有主提示词");
assert.equal(memories.length, 1);
assert.equal(memories[0]?.content, "当前主链优先打通主 Agent 聊天闭环。");
assert.deepEqual(memories[0]?.tags, ["聊天", "主Agent"]);
await archiveUserMasterMemory(created.memoryId, "17600003315");
const visible = await listUserMasterMemories("17600003315", { includeArchived: false });
const all = await listUserMasterMemories("17600003315", { includeArchived: true });
const archived = all.find((item) => item.memoryId === created.memoryId);
assert.equal(visible.length, 0);
assert.equal(archived?.archived, true);
assert.equal((await readState()).masterAgentMemories.length, 1);
});