feat: prioritize thread status in master agent prompt

This commit is contained in:
kris
2026-04-04 11:37:41 +08:00
parent f69eebd82d
commit 7d578aa12f
2 changed files with 305 additions and 22 deletions

View File

@@ -110,13 +110,28 @@ export async function resolveMasterAgentExecutionConfig(
"medium";
const promptPolicy = getMasterAgentPromptPolicyView(state);
const userPrompt = getUserMasterPromptView(state, resolvedAccountId);
const memoryScope = listUserMasterMemoriesView(state, resolvedAccountId, { includeArchived: false });
const memoryAccountIds = [...new Set([resolvedAccountId, state.user.account, runtime.account.accountId].filter(
(value): value is string => Boolean(value?.trim()),
))];
const memoryScope = [...new Map(
memoryAccountIds.flatMap((memoryAccountId) =>
listUserMasterMemoriesView(state, memoryAccountId, { includeArchived: false }),
).map((memory) => [memory.memoryId, memory] as const),
).values()];
const { projectMemories, userMemories } = resolveRuntimeRelevantMemories({
projectId,
requestText,
memories: memoryScope,
});
const touchedMemoryIds = [...projectMemories, ...userMemories].map((memory) => memory.memoryId);
const resolvedProjectMemories = [
...new Map(
[
...projectMemories,
...state.masterAgentMemories.filter((memory) => memory.scope === "project"),
].map((memory) => [memory.memoryId, memory] as const),
).values(),
].slice(0, 6);
const touchedMemoryIds = [...resolvedProjectMemories, ...userMemories].map((memory) => memory.memoryId);
if (touchedMemoryIds.length > 0) {
await touchUserMasterMemories(touchedMemoryIds, resolvedAccountId);
}
@@ -131,13 +146,13 @@ export async function resolveMasterAgentExecutionConfig(
reasoningEffort,
promptPolicy,
userPrompt,
projectMemories,
projectMemories: resolvedProjectMemories,
userMemories,
executionPrompt: buildExecutionPrompt({
globalPrompt: promptPolicy?.globalPrompt ?? null,
userPrompt: userPrompt?.content ?? null,
conversationPrompt: scopedAgentControls?.promptOverride ?? null,
projectMemories,
projectMemories: resolvedProjectMemories,
userMemories,
requestText: requestText ?? "",
}),
@@ -246,7 +261,27 @@ function buildRuntimeDigest(
.filter((update) => update.status === "available")
.map((update) => `${update.version} -> ${update.targetScope}`)
.join("\n");
const activeProjectUnderstandings = state.projects
const threadStatusDocuments = [...state.threadStatusDocuments]
.sort((left, right) => {
const updatedDelta = Date.parse(right.updatedAt) - Date.parse(left.updatedAt);
if (updatedDelta !== 0) {
return updatedDelta;
}
return right.documentId.localeCompare(left.documentId);
})
.slice(0, 6)
.map((document) => buildThreadStatusDocumentDigest(state, document));
const recentProgressEvents = [...state.threadProgressEvents]
.sort((left, right) => {
const createdDelta = Date.parse(right.createdAt) - Date.parse(left.createdAt);
if (createdDelta !== 0) {
return createdDelta;
}
return right.eventId.localeCompare(left.eventId);
})
.slice(0, 8)
.map((event) => buildThreadProgressEventDigest(state, event));
const deepPullThreadUnderstandings = state.projects
.filter((project) => project.id !== "master-agent" && project.projectUnderstanding)
.sort((left, right) =>
String(right.projectUnderstanding?.updatedAt ?? right.lastMessageAt).localeCompare(
@@ -254,20 +289,7 @@ function buildRuntimeDigest(
),
)
.slice(0, 3)
.map((project) => {
const understanding = project.projectUnderstanding!;
return [
`${project.name}`,
understanding.projectGoal ? `目标=${understanding.projectGoal}` : undefined,
understanding.currentProgress ? `进度=${understanding.currentProgress}` : undefined,
understanding.technicalArchitecture ? `架构=${understanding.technicalArchitecture}` : undefined,
understanding.currentBlockers ? `阻塞=${understanding.currentBlockers}` : undefined,
understanding.recommendedNextStep ? `下一步=${understanding.recommendedNextStep}` : undefined,
]
.filter(Boolean)
.join(" / ");
})
.join("\n");
.map((project) => buildDeepPullThreadUnderstandingDigest(project));
const authSummary = [
`登录会话策略:成功登录后默认保持 ${Math.round(AUTH_SESSION_TTL_MS / 24 / 60 / 60_000)} 天。`,
@@ -281,15 +303,21 @@ function buildRuntimeDigest(
`当前时间:${new Date().toISOString()}`,
`用户消息:${requestText}`,
"",
"线程状态文档:",
threadStatusDocuments.length > 0 ? threadStatusDocuments.join("\n") : "无",
"",
"最近进展事件:",
recentProgressEvents.length > 0 ? recentProgressEvents.join("\n") : "无",
"",
"关键时刻深拉线程兜底:",
deepPullThreadUnderstandings.length > 0 ? deepPullThreadUnderstandings.join("\n") : "无",
"",
"最近主 Agent 对话:",
recentMessages || "无",
"",
"最新 APP 日志:",
recentLogs || "无",
"",
"活跃项目理解:",
activeProjectUnderstandings || "无",
"",
"高风险线程:",
riskyThreads || "无",
"",
@@ -304,6 +332,64 @@ function buildRuntimeDigest(
].join("\n");
}
function buildThreadStatusDocumentDigest(
state: Awaited<ReturnType<typeof readState>>,
document: Awaited<ReturnType<typeof readState>>["threadStatusDocuments"][number],
) {
const projectName = state.projects.find((project) => project.id === document.projectId)?.name ?? document.projectId;
return [
`${projectName} / ${document.threadDisplayName}`,
document.folderName ? `文件夹=${document.folderName}` : undefined,
document.projectGoal ? `目标=${document.projectGoal}` : undefined,
document.currentPhase ? `阶段=${document.currentPhase}` : undefined,
document.currentProgress ? `进度=${document.currentProgress}` : undefined,
document.technicalArchitecture ? `架构=${document.technicalArchitecture}` : undefined,
document.currentBlockers ? `阻塞=${document.currentBlockers}` : undefined,
document.recommendedNextStep ? `下一步=${document.recommendedNextStep}` : undefined,
document.keyFiles.length > 0 ? `关键文件=${document.keyFiles.slice(0, 3).join(", ")}` : undefined,
document.keyCommands.length > 0 ? `关键命令=${document.keyCommands.slice(0, 2).join(", ")}` : undefined,
`更新时间=${document.updatedAt}`,
]
.filter(Boolean)
.join(" / ");
}
function buildThreadProgressEventDigest(
state: Awaited<ReturnType<typeof readState>>,
event: Awaited<ReturnType<typeof readState>>["threadProgressEvents"][number],
) {
const projectName = state.projects.find((project) => project.id === event.projectId)?.name ?? event.projectId;
return [
`${projectName} / ${event.threadDisplayName}`,
`时间=${event.createdAt}`,
`类型=${event.eventType}`,
`摘要=${event.summary}`,
event.phase ? `阶段=${event.phase}` : undefined,
event.blockerDelta ? `阻塞变化=${event.blockerDelta}` : undefined,
event.nextStepDelta ? `下一步变化=${event.nextStepDelta}` : undefined,
]
.filter(Boolean)
.join(" / ");
}
function buildDeepPullThreadUnderstandingDigest(project: Project) {
const understanding = project.projectUnderstanding;
if (!understanding) {
return "";
}
return [
`${project.name}`,
understanding.projectGoal ? `目标=${understanding.projectGoal}` : undefined,
understanding.currentProgress ? `进度=${understanding.currentProgress}` : undefined,
understanding.technicalArchitecture ? `架构=${understanding.technicalArchitecture}` : undefined,
understanding.currentBlockers ? `阻塞=${understanding.currentBlockers}` : undefined,
understanding.recommendedNextStep ? `下一步=${understanding.recommendedNextStep}` : undefined,
]
.filter(Boolean)
.join(" / ");
}
function extractResponseText(payload: unknown): string {
if (!payload || typeof payload !== "object") {
return "";

View File

@@ -0,0 +1,197 @@
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 writeState: (typeof import("../src/lib/boss-data"))["writeState"];
let saveAiAccount: (typeof import("../src/lib/boss-data"))["saveAiAccount"];
let updateMasterAgentPromptPolicy: (typeof import("../src/lib/boss-data"))["updateMasterAgentPromptPolicy"];
let updateUserMasterPrompt: (typeof import("../src/lib/boss-data"))["updateUserMasterPrompt"];
let createUserMasterMemory: (typeof import("../src/lib/boss-data"))["createUserMasterMemory"];
let updateProjectAgentControls: (typeof import("../src/lib/boss-data"))["updateProjectAgentControls"];
let resolveMasterAgentExecutionConfig: (typeof import("../src/lib/boss-master-agent"))["resolveMasterAgentExecutionConfig"];
let replyToMasterAgentUserMessage: (typeof import("../src/lib/boss-master-agent"))["replyToMasterAgentUserMessage"];
async function setup() {
if (runtimeRoot) return;
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-thread-status-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const [data, masterAgent] = await Promise.all([
import("../src/lib/boss-data.ts"),
import("../src/lib/boss-master-agent.ts"),
]);
readState = data.readState;
writeState = data.writeState;
saveAiAccount = data.saveAiAccount;
updateMasterAgentPromptPolicy = data.updateMasterAgentPromptPolicy;
updateUserMasterPrompt = data.updateUserMasterPrompt;
createUserMasterMemory = data.createUserMasterMemory;
updateProjectAgentControls = data.updateProjectAgentControls;
resolveMasterAgentExecutionConfig = masterAgent.resolveMasterAgentExecutionConfig;
replyToMasterAgentUserMessage = masterAgent.replyToMasterAgentUserMessage;
}
test.after(async () => {
if (runtimeRoot) {
await rm(runtimeRoot, { recursive: true, force: true });
}
});
test("主 Agent 执行 prompt 默认读取线程状态文档、最近进展事件和项目记忆,并保留深拉兜底", async () => {
await setup();
await saveAiAccount({
accountId: "master-codex-primary",
label: "主 GPT",
role: "primary",
provider: "master_codex_node",
displayName: "17600003315 · Master Codex Node",
nodeId: "mac-studio",
nodeLabel: "Mac Studio",
enabled: true,
setActive: true,
status: "ready",
loginStatusNote: "主节点可用。",
});
await updateMasterAgentPromptPolicy({
globalPrompt: "管理员全局主提示词",
updatedBy: "17600003315",
});
await updateUserMasterPrompt("17600003315", "用户私有主提示词");
await updateProjectAgentControls("master-agent", {
promptOverride: "当前对话提示词",
});
await createUserMasterMemory({
account: "17600003315",
scope: "project",
projectId: "master-agent",
title: "项目记忆",
content: "项目记忆正文",
memoryType: "project_progress",
tags: ["线程状态"],
});
await createUserMasterMemory({
account: "master-codex-primary",
scope: "project",
projectId: "master-agent",
title: "项目记忆",
content: "项目记忆正文",
memoryType: "project_progress",
tags: ["线程状态"],
});
const state = await readState();
const auditProject = state.projects.find((project) => project.id === "audit-collab");
assert.ok(auditProject, "expected seeded audit-collab project");
auditProject!.projectUnderstanding = {
projectGoal: "深拉兜底目标",
currentProgress: "深拉兜底进度",
technicalArchitecture: "深拉兜底架构",
currentBlockers: "深拉兜底阻塞",
recommendedNextStep: "深拉兜底下一步",
sourceTaskId: "task-deep-pull",
updatedAt: "2026-04-04T18:00:00+08:00",
sourceKind: "thread_sync",
};
state.threadStatusDocuments = [
{
documentId: "thread-status-doc-1",
projectId: "audit-collab",
threadId: "thread-audit-chief",
threadDisplayName: "审计对话",
folderName: "审计群聊",
deviceId: "mac-studio",
projectGoal: "线程状态目标",
currentPhase: "线程状态阶段",
currentProgress: "线程状态进度",
technicalArchitecture: "线程状态架构",
currentBlockers: "线程状态阻塞",
recommendedNextStep: "线程状态下一步",
keyFiles: ["src/lib/boss-master-agent.ts"],
keyCommands: ["npm run build"],
updatedAt: "2026-04-04T18:01:00+08:00",
sourceTaskId: "task-thread-status",
sourceKind: "incremental_sync",
},
];
state.threadProgressEvents = [
{
eventId: "thread-progress-event-1",
projectId: "audit-collab",
threadId: "thread-audit-chief",
threadDisplayName: "审计对话",
deviceId: "mac-studio",
eventType: "progress_updated",
summary: "最近进展事件摘要",
phase: "线程状态阶段",
blockerDelta: "线程状态阻塞",
nextStepDelta: "线程状态下一步",
createdAt: "2026-04-04T18:02:00+08:00",
sourceTaskId: "task-thread-progress",
},
];
await writeState(state);
const resolved = await resolveMasterAgentExecutionConfig(
"master-agent",
"17600003315",
"继续推进线程状态同步",
);
assert.ok(resolved.projectMemories.length > 0);
assert.equal(resolved.projectMemories[0]?.content, "项目记忆正文");
assert.ok(resolved.executionPrompt.includes("当前对话提示词"));
assert.ok(
resolved.executionPrompt.indexOf("管理员全局主提示词:") <
resolved.executionPrompt.indexOf("用户私有主提示词:") &&
resolved.executionPrompt.indexOf("用户私有主提示词:") <
resolved.executionPrompt.indexOf("当前对话附加提示词:") &&
resolved.executionPrompt.indexOf("当前对话附加提示词:") <
resolved.executionPrompt.indexOf("当前消息:"),
);
const reply = await replyToMasterAgentUserMessage({
requestText: "继续推进线程状态同步",
requestedBy: "Boss 超级管理员",
requestedByAccount: "17600003315",
mode: "enqueue",
});
assert.equal(reply.ok, true);
assert.equal(reply.masterReplyState, "queued");
const queuedTask = (await readState()).masterAgentTasks.find(
(task) => task.projectId === "master-agent" && task.requestText === "继续推进线程状态同步",
);
assert.ok(queuedTask, "expected master-agent task to be queued");
assert.ok(queuedTask?.executionPrompt.includes("线程状态文档:"));
assert.ok(queuedTask?.executionPrompt.includes("线程状态目标"));
assert.ok(queuedTask?.executionPrompt.includes("最近进展事件:"));
assert.ok(queuedTask?.executionPrompt.includes("最近进展事件摘要"));
assert.ok(queuedTask?.executionPrompt.includes("关键时刻深拉线程兜底:"));
assert.ok(queuedTask?.executionPrompt.includes("深拉兜底目标"));
assert.ok(
queuedTask?.executionPrompt.indexOf("管理员全局主提示词:") <
queuedTask.executionPrompt.indexOf("用户私有主提示词:") &&
queuedTask.executionPrompt.indexOf("用户私有主提示词:") <
queuedTask.executionPrompt.indexOf("当前对话附加提示词:") &&
queuedTask.executionPrompt.indexOf("当前对话附加提示词:") <
queuedTask.executionPrompt.indexOf("项目记忆:") &&
queuedTask.executionPrompt.indexOf("项目记忆:") <
queuedTask.executionPrompt.indexOf("当前消息:") &&
queuedTask.executionPrompt.indexOf("当前消息:") <
queuedTask.executionPrompt.indexOf("当前对话覆盖:") &&
queuedTask.executionPrompt.indexOf("当前对话覆盖:") <
queuedTask.executionPrompt.indexOf("线程状态文档:") &&
queuedTask.executionPrompt.indexOf("线程状态文档:") <
queuedTask.executionPrompt.indexOf("最近进展事件:") &&
queuedTask.executionPrompt.indexOf("最近进展事件:") <
queuedTask.executionPrompt.indexOf("关键时刻深拉线程兜底:"),
);
});