docs: add master-agent prompts and memory plan
This commit is contained in:
@@ -0,0 +1,998 @@
|
||||
# 主 Agent 提示词与记忆分层 Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 为主 Agent 建立分层提示词与用户级记忆体系,并在 Web 后台和原生 Android 聊天页提供完整的查看、编辑、自动沉淀和执行接入能力。
|
||||
|
||||
**Architecture:** 文件型状态中新增管理员全局主提示词、用户私有主提示词和用户记忆实体;`master-agent` 执行链统一合成全局提示词、用户提示词、当前对话附加提示词与相关记忆。Web 后台提供管理员全局主提示词编辑页,Android `master-agent` 聊天页右上角三点菜单新增 `提示词` 与 `记忆` 两个入口,对应独立原生页面完成前台管理。
|
||||
|
||||
**Tech Stack:** Next.js App Router, TypeScript, file-backed state store, Android AppCompat/Java, node:test, Gradle unit tests
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 为状态模型补齐主 Agent 提示词与用户记忆结构
|
||||
|
||||
**Files:**
|
||||
- Modify: `/Users/kris/code/boss/src/lib/boss-data.ts`
|
||||
- Modify: `/Users/kris/code/boss/src/lib/boss-projections.ts`
|
||||
- Test: `/Users/kris/code/boss/tests/master-agent-prompts-memory-state.test.ts`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,覆盖全局提示词、用户提示词、用户记忆的读写**
|
||||
|
||||
```ts
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
getMasterAgentPromptPolicy,
|
||||
updateMasterAgentPromptPolicy,
|
||||
getUserMasterPrompt,
|
||||
updateUserMasterPrompt,
|
||||
listUserMasterMemories,
|
||||
createUserMasterMemory,
|
||||
updateUserMasterMemory,
|
||||
archiveUserMasterMemory,
|
||||
} from "@/lib/boss-data";
|
||||
|
||||
test("主 Agent 提示词与用户记忆可读写", async () => {
|
||||
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 });
|
||||
assert.equal(visible.length, 0);
|
||||
assert.equal(all[0]?.archived, true);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-prompts-memory-state.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL ... getMasterAgentPromptPolicy is not a function
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 在状态模型中新增提示词与记忆结构**
|
||||
|
||||
```ts
|
||||
export interface MasterAgentPromptPolicy {
|
||||
globalPrompt: string;
|
||||
updatedAt: string;
|
||||
updatedBy?: string;
|
||||
}
|
||||
|
||||
export interface UserMasterPrompt {
|
||||
account: string;
|
||||
content: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export type MasterMemoryScope = "global" | "project";
|
||||
export type MasterMemoryType =
|
||||
| "user_preference"
|
||||
| "project_progress"
|
||||
| "decision"
|
||||
| "risk"
|
||||
| "blocking_issue"
|
||||
| "research_note"
|
||||
| "workflow_rule";
|
||||
|
||||
export interface MasterAgentMemory {
|
||||
memoryId: string;
|
||||
account: string;
|
||||
scope: MasterMemoryScope;
|
||||
projectId?: string;
|
||||
title: string;
|
||||
content: string;
|
||||
memoryType: MasterMemoryType;
|
||||
tags: string[];
|
||||
sourceMessageId?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
lastUsedAt?: string;
|
||||
archived: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 增加状态读写 helper**
|
||||
|
||||
```ts
|
||||
export async function getMasterAgentPromptPolicy() {
|
||||
const state = await readState();
|
||||
return state.masterAgentPromptPolicy ?? null;
|
||||
}
|
||||
|
||||
export async function updateMasterAgentPromptPolicy(input: {
|
||||
globalPrompt: string;
|
||||
updatedBy?: string;
|
||||
}) {
|
||||
return withStateLock(async (state) => {
|
||||
state.masterAgentPromptPolicy = {
|
||||
globalPrompt: input.globalPrompt.trim(),
|
||||
updatedBy: input.updatedBy,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
return state.masterAgentPromptPolicy;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getUserMasterPrompt(account: string) {
|
||||
const state = await readState();
|
||||
return state.userMasterPrompts.find((item) => item.account === account) ?? null;
|
||||
}
|
||||
|
||||
export async function updateUserMasterPrompt(account: string, content: string) {
|
||||
return withStateLock(async (state) => {
|
||||
const existing = state.userMasterPrompts.find((item) => item.account === account);
|
||||
const next = {
|
||||
account,
|
||||
content: content.trim(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
if (existing) Object.assign(existing, next);
|
||||
else state.userMasterPrompts.unshift(next);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 增加用户记忆 CRUD helper**
|
||||
|
||||
```ts
|
||||
export async function createUserMasterMemory(input: {
|
||||
account: string;
|
||||
scope: MasterMemoryScope;
|
||||
projectId?: string;
|
||||
title: string;
|
||||
content: string;
|
||||
memoryType: MasterMemoryType;
|
||||
tags?: string[];
|
||||
sourceMessageId?: string;
|
||||
}) {
|
||||
return withStateLock(async (state) => {
|
||||
const now = new Date().toISOString();
|
||||
const memory: MasterAgentMemory = {
|
||||
memoryId: createStableId("memory"),
|
||||
account: input.account,
|
||||
scope: input.scope,
|
||||
projectId: input.scope === "project" ? input.projectId : undefined,
|
||||
title: input.title.trim(),
|
||||
content: input.content.trim(),
|
||||
memoryType: input.memoryType,
|
||||
tags: normalizeTags(input.tags ?? []),
|
||||
sourceMessageId: input.sourceMessageId,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
lastUsedAt: now,
|
||||
archived: false,
|
||||
};
|
||||
state.masterAgentMemories.unshift(memory);
|
||||
return memory;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 再跑测试确认通过**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-prompts-memory-state.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
# tests 1
|
||||
# pass 1
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
git add tests/master-agent-prompts-memory-state.test.ts src/lib/boss-data.ts src/lib/boss-projections.ts
|
||||
git commit -m "feat: add master-agent prompt and memory state"
|
||||
```
|
||||
|
||||
### Task 2: 接入主 Agent 执行链,真正合成三层提示词与用户记忆
|
||||
|
||||
**Files:**
|
||||
- Modify: `/Users/kris/code/boss/src/lib/boss-master-agent.ts`
|
||||
- Test: `/Users/kris/code/boss/tests/master-agent-prompt-assembly.test.ts`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,覆盖提示词和记忆的执行合成顺序**
|
||||
|
||||
```ts
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
updateMasterAgentPromptPolicy,
|
||||
updateUserMasterPrompt,
|
||||
updateProjectAgentControls,
|
||||
createUserMasterMemory,
|
||||
} from "@/lib/boss-data";
|
||||
import { buildMasterAgentExecutionEnvelopeForTesting } from "@/lib/boss-master-agent";
|
||||
|
||||
test("主 Agent 执行上下文按全局提示词 -> 用户提示词 -> 对话提示词 -> 相关记忆拼装", async () => {
|
||||
await updateMasterAgentPromptPolicy({
|
||||
globalPrompt: "全局规则:回复务必中文。",
|
||||
updatedBy: "17600003315",
|
||||
});
|
||||
await updateUserMasterPrompt("17600003315", "用户规则:尽量直接给下一步。");
|
||||
await updateProjectAgentControls("master-agent", {
|
||||
promptOverride: "当前对话:优先聚焦主 Agent 聊天链。",
|
||||
});
|
||||
await createUserMasterMemory({
|
||||
account: "17600003315",
|
||||
scope: "global",
|
||||
title: "长期偏好",
|
||||
content: "用户偏好微信式交互。",
|
||||
memoryType: "user_preference",
|
||||
tags: ["UI"],
|
||||
});
|
||||
await createUserMasterMemory({
|
||||
account: "17600003315",
|
||||
scope: "project",
|
||||
projectId: "master-agent",
|
||||
title: "当前主链",
|
||||
content: "当前优先打通主 Agent 聊天体验。",
|
||||
memoryType: "project_progress",
|
||||
tags: ["聊天"],
|
||||
});
|
||||
|
||||
const digest = await buildMasterAgentExecutionEnvelopeForTesting({
|
||||
projectId: "master-agent",
|
||||
account: "17600003315",
|
||||
requestText: "下一步做什么?",
|
||||
});
|
||||
|
||||
assert.match(digest, /全局规则:回复务必中文/);
|
||||
assert.match(digest, /用户规则:尽量直接给下一步/);
|
||||
assert.match(digest, /当前对话:优先聚焦主 Agent 聊天链/);
|
||||
assert.match(digest, /用户偏好微信式交互/);
|
||||
assert.match(digest, /当前优先打通主 Agent 聊天体验/);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-prompt-assembly.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL ... buildMasterAgentExecutionEnvelopeForTesting is not a function
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 在 `boss-master-agent.ts` 中增加提示词/记忆解析函数**
|
||||
|
||||
```ts
|
||||
async function resolveMasterAgentPromptLayers(account: string, projectId: string) {
|
||||
const [policy, userPrompt, controls, memories] = await Promise.all([
|
||||
getMasterAgentPromptPolicy(),
|
||||
getUserMasterPrompt(account),
|
||||
getProjectAgentControls(projectId),
|
||||
listUserMasterMemories(account, {
|
||||
includeArchived: false,
|
||||
projectId,
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
globalPrompt: policy?.globalPrompt?.trim() || "",
|
||||
userPrompt: userPrompt?.content?.trim() || "",
|
||||
conversationPrompt: controls?.promptOverride?.trim() || "",
|
||||
memories,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 实现最终 prompt/记忆组合**
|
||||
|
||||
```ts
|
||||
function buildPromptAssembly(input: {
|
||||
globalPrompt: string;
|
||||
userPrompt: string;
|
||||
conversationPrompt: string;
|
||||
memories: MasterAgentMemory[];
|
||||
}) {
|
||||
const sections = [
|
||||
input.globalPrompt && `【管理员全局主提示词】\n${input.globalPrompt}`,
|
||||
input.userPrompt && `【用户私有主提示词】\n${input.userPrompt}`,
|
||||
input.conversationPrompt && `【当前对话附加提示词】\n${input.conversationPrompt}`,
|
||||
input.memories.length
|
||||
? `【记忆】\n${input.memories.map((item) => `- ${item.title}:${item.content}`).join("\n")}`
|
||||
: "",
|
||||
].filter(Boolean);
|
||||
|
||||
return sections.join("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 把组合结果接到 OpenAI / Master Node 执行路径**
|
||||
|
||||
```ts
|
||||
const promptLayers = await resolveMasterAgentPromptLayers(params.requestedByAccount ?? "", "master-agent");
|
||||
const assembledPrompt = buildPromptAssembly(promptLayers);
|
||||
|
||||
const instructions = [
|
||||
buildMasterAgentInstructions(),
|
||||
assembledPrompt,
|
||||
].filter(Boolean).join("\n\n");
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 再跑测试确认通过**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-prompt-assembly.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
# tests 1
|
||||
# pass 1
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
git add tests/master-agent-prompt-assembly.test.ts src/lib/boss-master-agent.ts
|
||||
git commit -m "feat: apply layered prompts and memories to master-agent"
|
||||
```
|
||||
|
||||
### Task 3: 加入自动记忆沉淀、项目/用户分流与基础去重
|
||||
|
||||
**Files:**
|
||||
- Modify: `/Users/kris/code/boss/src/lib/boss-master-agent.ts`
|
||||
- Modify: `/Users/kris/code/boss/src/lib/boss-data.ts`
|
||||
- Test: `/Users/kris/code/boss/tests/master-agent-memory-ingestion.test.ts`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,覆盖项目记忆和用户记忆自动分流**
|
||||
|
||||
```ts
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { ingestMasterAgentMemoriesForTesting } from "@/lib/boss-master-agent";
|
||||
import { listUserMasterMemories } from "@/lib/boss-data";
|
||||
|
||||
test("自动记忆沉淀会按项目进度与用户偏好分流", async () => {
|
||||
await ingestMasterAgentMemoriesForTesting({
|
||||
account: "17600003315",
|
||||
projectId: "master-agent",
|
||||
userMessage: "我更喜欢微信式交互,这个项目当前重点是主 Agent 聊天主链。",
|
||||
replyText: "收到。",
|
||||
});
|
||||
|
||||
const memories = await listUserMasterMemories("17600003315", { includeArchived: false });
|
||||
assert.ok(memories.some((item) => item.scope === "global" && /微信式交互/.test(item.content)));
|
||||
assert.ok(memories.some((item) => item.scope === "project" && item.projectId === "master-agent" && /主 Agent 聊天主链/.test(item.content)));
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-memory-ingestion.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL ... ingestMasterAgentMemoriesForTesting is not a function
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 实现基础自动分类规则**
|
||||
|
||||
```ts
|
||||
function inferMemoryCandidates(input: { userMessage: string; projectId: string }) {
|
||||
const candidates = [];
|
||||
if (/微信式交互|中文回复|直接给下一步/.test(input.userMessage)) {
|
||||
candidates.push({
|
||||
scope: "global",
|
||||
memoryType: "user_preference",
|
||||
title: "用户偏好",
|
||||
content: input.userMessage,
|
||||
});
|
||||
}
|
||||
if (/项目|主链|阻塞|进度|决策/.test(input.userMessage)) {
|
||||
candidates.push({
|
||||
scope: "project",
|
||||
projectId: input.projectId,
|
||||
memoryType: "project_progress",
|
||||
title: "项目进度",
|
||||
content: input.userMessage,
|
||||
});
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 实现基础去重/合并**
|
||||
|
||||
```ts
|
||||
function isSimilarMemory(existing: MasterAgentMemory, incoming: {
|
||||
scope: MasterMemoryScope;
|
||||
projectId?: string;
|
||||
title: string;
|
||||
memoryType: MasterMemoryType;
|
||||
}) {
|
||||
return (
|
||||
existing.scope === incoming.scope &&
|
||||
(existing.projectId || "") === (incoming.projectId || "") &&
|
||||
existing.memoryType === incoming.memoryType &&
|
||||
existing.title === incoming.title
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 在主 Agent 回复完成链路里接自动沉淀**
|
||||
|
||||
```ts
|
||||
if (params.requestedByAccount) {
|
||||
await maybeIngestMasterAgentMemories({
|
||||
account: params.requestedByAccount,
|
||||
projectId: "master-agent",
|
||||
userMessage: params.requestText,
|
||||
replyText: generated.content,
|
||||
sourceMessageId: params.requestMessageId,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 再跑测试确认通过**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-memory-ingestion.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
# tests 1
|
||||
# pass 1
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
git add tests/master-agent-memory-ingestion.test.ts src/lib/boss-master-agent.ts src/lib/boss-data.ts
|
||||
git commit -m "feat: auto-ingest master-agent memories"
|
||||
```
|
||||
|
||||
### Task 4: 新增管理员全局主提示词 Web 后台接口与页面
|
||||
|
||||
**Files:**
|
||||
- Create: `/Users/kris/code/boss/src/app/api/v1/master-agent/prompt-policy/route.ts`
|
||||
- Create: `/Users/kris/code/boss/src/app/me/ops/master-agent/page.tsx`
|
||||
- Test: `/Users/kris/code/boss/tests/master-agent-prompt-policy-route.test.ts`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,覆盖管理员可读写全局主提示词**
|
||||
|
||||
```ts
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { NextRequest } from "next/server";
|
||||
import { GET, POST } from "@/app/api/v1/master-agent/prompt-policy/route";
|
||||
|
||||
test("管理员可读写全局主提示词", async () => {
|
||||
const post = await POST(new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/prompt-policy", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
cookie: await createAdminCookieForTesting(),
|
||||
},
|
||||
body: JSON.stringify({ globalPrompt: "全局主提示词" }),
|
||||
}));
|
||||
assert.equal(post.status, 200);
|
||||
|
||||
const get = await GET(new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/prompt-policy", {
|
||||
headers: { cookie: await createAdminCookieForTesting() },
|
||||
}));
|
||||
const payload = await get.json();
|
||||
assert.equal(payload.policy.globalPrompt, "全局主提示词");
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-prompt-policy-route.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL ... Cannot find module ... prompt-policy/route
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 实现路由**
|
||||
|
||||
```ts
|
||||
export async function GET(request: NextRequest) {
|
||||
const session = await requireRequestSession(request);
|
||||
if (!session) return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||
if (session.role !== "highest_admin") {
|
||||
return NextResponse.json({ ok: false, message: "FORBIDDEN" }, { status: 403 });
|
||||
}
|
||||
const policy = await getMasterAgentPromptPolicy();
|
||||
return NextResponse.json({ ok: true, policy });
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 实现 Web 后台页面**
|
||||
|
||||
```tsx
|
||||
export default async function MasterAgentPromptPolicyPage() {
|
||||
const policy = await getMasterAgentPromptPolicy();
|
||||
return (
|
||||
<main className="mx-auto max-w-3xl p-6">
|
||||
<h1 className="text-xl font-semibold">主 Agent 全局主提示词</h1>
|
||||
<p className="mt-2 text-sm text-slate-500">这层提示词对所有用户生效,且不可被覆盖。</p>
|
||||
<PromptPolicyEditor initialPrompt={policy?.globalPrompt ?? ""} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 再跑测试确认通过**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-prompt-policy-route.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
# tests 1
|
||||
# pass 1
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
git add src/app/api/v1/master-agent/prompt-policy/route.ts src/app/me/ops/master-agent/page.tsx tests/master-agent-prompt-policy-route.test.ts
|
||||
git commit -m "feat: add master-agent global prompt policy"
|
||||
```
|
||||
|
||||
### Task 5: 新增用户提示词与记忆 API
|
||||
|
||||
**Files:**
|
||||
- Create: `/Users/kris/code/boss/src/app/api/v1/master-agent/user-prompt/route.ts`
|
||||
- Create: `/Users/kris/code/boss/src/app/api/v1/master-agent/memories/route.ts`
|
||||
- Create: `/Users/kris/code/boss/src/app/api/v1/master-agent/memories/[memoryId]/route.ts`
|
||||
- Test: `/Users/kris/code/boss/tests/master-agent-memory-routes.test.ts`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,覆盖用户私有提示词与记忆 CRUD**
|
||||
|
||||
```ts
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
test("用户可更新私有提示词并维护主 Agent 记忆", async () => {
|
||||
const cookie = await createAdminCookieForTesting();
|
||||
const promptResponse = await postUserPrompt(cookie, "请始终用中文直接回复");
|
||||
assert.equal(promptResponse.status, 200);
|
||||
|
||||
const created = await postMemory(cookie, {
|
||||
scope: "global",
|
||||
title: "用户偏好",
|
||||
content: "偏好微信式交互",
|
||||
memoryType: "user_preference",
|
||||
});
|
||||
assert.equal(created.status, 200);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-memory-routes.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL ... Cannot find module ... /user-prompt/route
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 实现用户私有提示词路由**
|
||||
|
||||
```ts
|
||||
export async function POST(request: NextRequest) {
|
||||
const session = await requireRequestSession(request);
|
||||
if (!session) return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||
const body = await request.json();
|
||||
const prompt = await updateUserMasterPrompt(session.account, String(body.content ?? ""));
|
||||
return NextResponse.json({ ok: true, prompt });
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 实现记忆列表与新增路由**
|
||||
|
||||
```ts
|
||||
export async function GET(request: NextRequest) {
|
||||
const session = await requireRequestSession(request);
|
||||
if (!session) return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||
const { searchParams } = new URL(request.url);
|
||||
const projectId = searchParams.get("projectId") ?? undefined;
|
||||
const includeArchived = searchParams.get("includeArchived") === "true";
|
||||
const memories = await listUserMasterMemories(session.account, { projectId, includeArchived });
|
||||
return NextResponse.json({ ok: true, memories });
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 实现单条记忆编辑与删除路由**
|
||||
|
||||
```ts
|
||||
export async function POST(request: NextRequest, context: { params: Promise<{ memoryId: string }> }) {
|
||||
const session = await requireRequestSession(request);
|
||||
if (!session) return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||
const { memoryId } = await context.params;
|
||||
const body = await request.json();
|
||||
const memory = await updateUserMasterMemory(memoryId, session.account, body);
|
||||
return NextResponse.json({ ok: true, memory });
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 再跑测试确认通过**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-memory-routes.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
# tests 1
|
||||
# pass 1
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
git add src/app/api/v1/master-agent/user-prompt/route.ts src/app/api/v1/master-agent/memories/route.ts src/app/api/v1/master-agent/memories/[memoryId]/route.ts tests/master-agent-memory-routes.test.ts
|
||||
git commit -m "feat: add master-agent prompt and memory routes"
|
||||
```
|
||||
|
||||
### Task 6: Android 三点菜单新增提示词与记忆入口,并实现原生提示词页
|
||||
|
||||
**Files:**
|
||||
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java`
|
||||
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/BossApiClient.java`
|
||||
- Create: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/MasterAgentPromptActivity.java`
|
||||
- Test: `/Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityMasterAgentMenuTest.java`
|
||||
- Test: `/Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/MasterAgentPromptActivityTest.java`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,覆盖三点菜单新增项**
|
||||
|
||||
```java
|
||||
@Test
|
||||
public void masterAgentMenuShowsPromptAndMemoryEntries() {
|
||||
ProjectDetailActivity activity = Robolectric.buildActivity(ProjectDetailActivity.class).setup().get();
|
||||
ReflectionHelpers.setField(activity, "projectId", "master-agent");
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showMasterAgentMoreMenu");
|
||||
|
||||
AlertDialog dialog = ShadowAlertDialog.getLatestAlertDialog();
|
||||
ListView listView = dialog.getListView();
|
||||
assertMenuItem(listView, 0, "提示词");
|
||||
assertMenuItem(listView, 1, "记忆");
|
||||
assertMenuItem(listView, 2, "模型");
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss/android
|
||||
./gradlew testDebugUnitTest --tests com.hyzq.boss.ProjectDetailActivityMasterAgentMenuTest --no-daemon
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL ... expected 提示词
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 扩展 Android API 客户端**
|
||||
|
||||
```java
|
||||
public ApiResponse getMasterAgentPromptPolicy() throws IOException, JSONException {
|
||||
return requestWithRestore("GET", "/api/v1/master-agent/prompt-policy", null);
|
||||
}
|
||||
|
||||
public ApiResponse getUserMasterPrompt() throws IOException, JSONException {
|
||||
return requestWithRestore("GET", "/api/v1/master-agent/user-prompt", null);
|
||||
}
|
||||
|
||||
public ApiResponse updateUserMasterPrompt(String content) throws IOException, JSONException {
|
||||
JSONObject payload = new JSONObject().put("content", content);
|
||||
return requestWithRestore("POST", "/api/v1/master-agent/user-prompt", payload);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 把聊天页三点菜单扩成提示词/记忆/模型/推理强度/会话信息/刷新**
|
||||
|
||||
```java
|
||||
new AlertDialog.Builder(this)
|
||||
.setItems(new CharSequence[]{"提示词", "记忆", "模型", "推理强度", "会话信息", "刷新"}, (dialog, which) -> {
|
||||
if (which == 0) openMasterAgentPrompt();
|
||||
else if (which == 1) openMasterAgentMemories();
|
||||
else if (which == 2) showMasterAgentModelPicker();
|
||||
else if (which == 3) showMasterAgentReasoningPicker();
|
||||
else if (which == 4) openConversationInfo();
|
||||
else reload(true);
|
||||
})
|
||||
.show();
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 实现原生提示词页**
|
||||
|
||||
```java
|
||||
public class MasterAgentPromptActivity extends BossScreenActivity {
|
||||
// 显示管理员全局主提示词(只读)
|
||||
// 显示我的主提示词(可编辑)
|
||||
// 显示当前对话附加提示词(可编辑)
|
||||
// 显示“查看组合结果”
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 再跑 Android 单测确认通过**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss/android
|
||||
./gradlew testDebugUnitTest --tests com.hyzq.boss.ProjectDetailActivityMasterAgentMenuTest --tests com.hyzq.boss.MasterAgentPromptActivityTest --no-daemon
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
BUILD SUCCESSFUL
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
git add android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java android/app/src/main/java/com/hyzq/boss/BossApiClient.java android/app/src/main/java/com/hyzq/boss/MasterAgentPromptActivity.java android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityMasterAgentMenuTest.java android/app/src/test/java/com/hyzq/boss/MasterAgentPromptActivityTest.java
|
||||
git commit -m "feat: add master-agent prompt editor on android"
|
||||
```
|
||||
|
||||
### Task 7: 实现原生记忆页,支持新增/编辑/删除
|
||||
|
||||
**Files:**
|
||||
- Modify: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/BossApiClient.java`
|
||||
- Create: `/Users/kris/code/boss/android/app/src/main/java/com/hyzq/boss/MasterAgentMemoriesActivity.java`
|
||||
- Test: `/Users/kris/code/boss/android/app/src/test/java/com/hyzq/boss/MasterAgentMemoriesActivityTest.java`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,覆盖记忆页的新增、编辑、删除入口**
|
||||
|
||||
```java
|
||||
@Test
|
||||
public void memoriesScreenShowsAddEditDeleteActions() {
|
||||
MasterAgentMemoriesActivity activity = Robolectric.buildActivity(MasterAgentMemoriesActivity.class).setup().get();
|
||||
LinearLayout root = activity.findViewById(R.id.screen_content_container);
|
||||
assertTrue(viewTreeContainsText(root, "新增记忆"));
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss/android
|
||||
./gradlew testDebugUnitTest --tests com.hyzq.boss.MasterAgentMemoriesActivityTest --no-daemon
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL ... MasterAgentMemoriesActivity not found
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 扩展 Android API 客户端接记忆路由**
|
||||
|
||||
```java
|
||||
public ApiResponse listMasterAgentMemories(@Nullable String projectId) throws IOException, JSONException {
|
||||
String path = "/api/v1/master-agent/memories";
|
||||
if (projectId != null && !projectId.isEmpty()) {
|
||||
path += "?projectId=" + Uri.encode(projectId);
|
||||
}
|
||||
return requestWithRestore("GET", path, null);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 实现原生记忆页**
|
||||
|
||||
```java
|
||||
public class MasterAgentMemoriesActivity extends BossScreenActivity {
|
||||
// section 1: 项目记忆
|
||||
// section 2: 我的通用记忆
|
||||
// 每条卡片支持编辑/删除
|
||||
// 顶部提供新增记忆
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 实现新增/编辑/删除对话框**
|
||||
|
||||
```java
|
||||
private void showMemoryEditor(@Nullable JSONObject memory) {
|
||||
// title + content + scope + memoryType + tags
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 再跑 Android 单测确认通过**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss/android
|
||||
./gradlew testDebugUnitTest --tests com.hyzq.boss.MasterAgentMemoriesActivityTest --no-daemon
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
BUILD SUCCESSFUL
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
git add android/app/src/main/java/com/hyzq/boss/BossApiClient.java android/app/src/main/java/com/hyzq/boss/MasterAgentMemoriesActivity.java android/app/src/test/java/com/hyzq/boss/MasterAgentMemoriesActivityTest.java
|
||||
git commit -m "feat: add master-agent memories screen on android"
|
||||
```
|
||||
|
||||
### Task 8: 同步 Web 前台、Android Manifest、文档并做总验证
|
||||
|
||||
**Files:**
|
||||
- Modify: `/Users/kris/code/boss/android/app/src/main/AndroidManifest.xml`
|
||||
- Modify: `/Users/kris/code/boss/README.md`
|
||||
- Modify: `/Users/kris/code/boss/docs/architecture/current_runtime_and_deploy_status_cn.md`
|
||||
- Modify: `/Users/kris/code/boss/docs/architecture/api_and_service_inventory_cn.md`
|
||||
|
||||
- [ ] **Step 1: 在 Android Manifest 注册新增页面**
|
||||
|
||||
```xml
|
||||
<activity android:name=".MasterAgentPromptActivity" android:exported="false" />
|
||||
<activity android:name=".MasterAgentMemoriesActivity" android:exported="false" />
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 同步 README 和架构文档**
|
||||
|
||||
```md
|
||||
- 主 Agent 聊天页右上角当前已新增 `提示词 / 记忆 / 模型 / 推理强度 / 会话信息 / 刷新`
|
||||
- 管理员全局主提示词当前由 Web 后台配置,用户端只读
|
||||
- 用户记忆当前分为项目记忆和用户通用记忆,并支持自动沉淀与手动管理
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 跑 Node 测试**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npx --yes tsx --test tests/master-agent-prompts-memory-state.test.ts tests/master-agent-prompt-assembly.test.ts tests/master-agent-memory-ingestion.test.ts tests/master-agent-prompt-policy-route.test.ts tests/master-agent-memory-routes.test.ts tests/master-agent-chat-controls.test.ts tests/master-agent-message-queue.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
all tests pass
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 跑 Android 单测**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss/android
|
||||
./gradlew testDebugUnitTest --tests com.hyzq.boss.ProjectDetailActivityMasterAgentMenuTest --tests com.hyzq.boss.MasterAgentPromptActivityTest --tests com.hyzq.boss.MasterAgentMemoriesActivityTest --tests com.hyzq.boss.AiAccountsActivityTest --no-daemon
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
BUILD SUCCESSFUL
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 跑 lint 和 build**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
npm run lint
|
||||
npm run build
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
lint 通过
|
||||
build 通过
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/kris/code/boss
|
||||
git add android/app/src/main/AndroidManifest.xml README.md docs/architecture/current_runtime_and_deploy_status_cn.md docs/architecture/api_and_service_inventory_cn.md
|
||||
git commit -m "docs: sync master-agent prompts and memories runtime"
|
||||
```
|
||||
Reference in New Issue
Block a user