feat: add master agent evolution engine core
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
7. `docs/architecture/thread_context_budget_and_handoff_protocol_cn.md`
|
7. `docs/architecture/thread_context_budget_and_handoff_protocol_cn.md`
|
||||||
8. `prompts/codex_fullstack_build_and_deploy_prompt_cn.md`
|
8. `prompts/codex_fullstack_build_and_deploy_prompt_cn.md`
|
||||||
9. `docs/superpowers/specs/2026-04-16-master-agent-fast-path-design.md`
|
9. `docs/superpowers/specs/2026-04-16-master-agent-fast-path-design.md`
|
||||||
|
10. `docs/superpowers/specs/2026-04-16-master-agent-evolution-engine-design.md`
|
||||||
|
|
||||||
## 3. 当前有效实现边界
|
## 3. 当前有效实现边界
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
- `src/lib/boss-device-auth.ts`:设备 token / 登录会话混合鉴权辅助
|
- `src/lib/boss-device-auth.ts`:设备 token / 登录会话混合鉴权辅助
|
||||||
- `src/lib/boss-events.ts`:SSE 事件总线
|
- `src/lib/boss-events.ts`:SSE 事件总线
|
||||||
- `src/lib/boss-master-agent.ts`:主 Agent 真实回复链路、Master Codex Node relay 与 API 容灾逻辑
|
- `src/lib/boss-master-agent.ts`:主 Agent 真实回复链路、Master Codex Node relay 与 API 容灾逻辑
|
||||||
|
- `src/lib/master-agent-evolution.ts`:主 Agent 自动进化引擎,负责 signal、proposal、rule 与 controlled / autonomous 模式
|
||||||
- `src/lib/boss-attachments.ts`:附件类型识别、分析状态决策和下载头
|
- `src/lib/boss-attachments.ts`:附件类型识别、分析状态决策和下载头
|
||||||
- `src/lib/boss-storage.ts`:附件存储抽象、配置校验和脱敏输出
|
- `src/lib/boss-storage.ts`:附件存储抽象、配置校验和脱敏输出
|
||||||
- `src/lib/boss-storage-server-file.ts`:服务器文件存储上传 / 读取
|
- `src/lib/boss-storage-server-file.ts`:服务器文件存储上传 / 读取
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ cd /Users/kris/code/boss
|
|||||||
- 主 Agent 当前真实对话链路已验证通过:`Boss Web -> /api/v1/projects/master-agent/messages -> master-agent task queue -> local-agent -> codex exec -> /complete -> 项目消息账本`
|
- 主 Agent 当前真实对话链路已验证通过:`Boss Web -> /api/v1/projects/master-agent/messages -> master-agent task queue -> local-agent -> codex exec -> /complete -> 项目消息账本`
|
||||||
- 主 Agent 单聊当前已改成“快速入队 + 异步回流”:`POST /api/v1/projects/master-agent/messages` 会先返回 `masterReplyState + task`,真实回复随后再回写消息账本
|
- 主 Agent 单聊当前已改成“快速入队 + 异步回流”:`POST /api/v1/projects/master-agent/messages` 会先返回 `masterReplyState + task`,真实回复随后再回写消息账本
|
||||||
- 当前对话级 `agentControls` 已经生效:`master-agent` 会话支持 `modelOverride / reasoningEffortOverride` 强制覆盖,也支持 `fastModelOverride / fastReasoningEffortOverride / smartModelOverride / smartReasoningEffortOverride` 这组策略默认值;主 Agent 普通对话默认按 fast 档选模型,深度任务可按 smart 档选模型,手动强制覆盖仍然优先级最高
|
- 当前对话级 `agentControls` 已经生效:`master-agent` 会话支持 `modelOverride / reasoningEffortOverride` 强制覆盖,也支持 `fastModelOverride / fastReasoningEffortOverride / smartModelOverride / smartReasoningEffortOverride` 这组策略默认值;主 Agent 普通对话默认按 fast 档选模型,深度任务可按 smart 档选模型,手动强制覆盖仍然优先级最高
|
||||||
|
- 当前主 Agent 自动进化引擎已接入第一阶段共享内核:`masterAgentEvolutionConfig / Signals / Proposals / Rules / RunLogs` 已进入状态模型,`GET /api/v1/master-agent/evolution` 可查看进化状态,`POST /api/v1/master-agent/evolution/config` 可在 `controlled / autonomous` 间切换,提案支持 approve / reject;`controlled` 只生成待审批提案,`autonomous` 可自动采纳低风险 `memory_patch / routing_preference_patch / fast_path_rule`
|
||||||
- 当前 `group_dispatch_plan / device_import_resolution / attachment_analysis` 三类深度任务已经会把 `smart*` 策略下发到任务队列,并随任务持久化 `executionModel / executionReasoningEffort`;local-agent 执行这类任务时会优先吃任务级模型,不再只依赖本机固定默认模型
|
- 当前 `group_dispatch_plan / device_import_resolution / attachment_analysis` 三类深度任务已经会把 `smart*` 策略下发到任务队列,并随任务持久化 `executionModel / executionReasoningEffort`;local-agent 执行这类任务时会优先吃任务级模型,不再只依赖本机固定默认模型
|
||||||
- 当前对话级 `agentControls` 也已支持 `backendOverride`:`master-agent` 会话可在 `Claw Runtime` 或 `Hermes Runtime` 可用时显式选择 `claw-runtime / hermes-runtime`,普通单线程会话当前只开放 `hermes-runtime`;不可用时保存接口会直接拒绝,并返回人类可读原因
|
- 当前对话级 `agentControls` 也已支持 `backendOverride`:`master-agent` 会话可在 `Claw Runtime` 或 `Hermes Runtime` 可用时显式选择 `claw-runtime / hermes-runtime`,普通单线程会话当前只开放 `hermes-runtime`;不可用时保存接口会直接拒绝,并返回人类可读原因
|
||||||
- 原生 Android 当前会把 `master-agent` 的等待态保留在消息流里:发送后常驻显示“主 Agent 思考中”,超时后改成“主 Agent 回复超时 + 重试等待”,收到新回复后会自动清掉,不再只靠 toast 提示
|
- 原生 Android 当前会把 `master-agent` 的等待态保留在消息流里:发送后常驻显示“主 Agent 思考中”,超时后改成“主 Agent 回复超时 + 重试等待”,收到新回复后会自动清掉,不再只靠 toast 提示
|
||||||
|
|||||||
@@ -0,0 +1,287 @@
|
|||||||
|
# Master Agent Evolution Engine 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:** 给 Boss 主Agent 增加一套统一的自动进化引擎,并支持 `controlled` 与 `autonomous` 两种模式以及双分支交付。
|
||||||
|
|
||||||
|
**Architecture:** 在现有 `boss-data` 与 `boss-master-agent` 上新增 evolution state、engine 和 API。共享内核一次实现,分支差异只保留在默认模式与自动采纳开关上,避免维护两份逻辑。
|
||||||
|
|
||||||
|
**Tech Stack:** Next.js 16、TypeScript、文件型状态存储、Node test runner
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: 增加 Evolution 状态模型与持久化
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/src/lib/boss-data.ts`
|
||||||
|
- Test: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/tests/master-agent-evolution-state.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写失败测试,定义 evolution state 的默认结构与读写**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
test("boss state 初始化时包含 master agent evolution 默认配置与空集合", async () => {
|
||||||
|
const state = await readState();
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionConfig?.mode, "controlled");
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionSignals, []);
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionProposals, []);
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionRules, []);
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionRunLogs, []);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行测试,确认当前失败**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-state.test.ts`
|
||||||
|
Expected: FAIL,提示缺少 `masterAgentEvolution*` 字段或断言不匹配
|
||||||
|
|
||||||
|
- [ ] **Step 3: 在 `boss-data.ts` 中新增类型和默认状态**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export type MasterAgentEvolutionMode = "controlled" | "autonomous";
|
||||||
|
|
||||||
|
export interface MasterAgentEvolutionConfig {
|
||||||
|
mode: MasterAgentEvolutionMode;
|
||||||
|
autoApplyLowRiskRules: boolean;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 扩展 `BossState`、normalize 逻辑和默认初始值**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-state.test.ts`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 5: 提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add src/lib/boss-data.ts tests/master-agent-evolution-state.test.ts
|
||||||
|
git commit -m "feat: add master agent evolution state"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 2: 实现 Evolution Engine 核心
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/src/lib/master-agent-evolution.ts`
|
||||||
|
- Test: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/tests/master-agent-evolution-engine.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写失败测试,覆盖 signal -> proposal 的最小闭环**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
test("recording repeated deterministic questions creates a fast_path_rule proposal", async () => {
|
||||||
|
await recordMasterAgentEvolutionSignal({
|
||||||
|
kind: "repeated_question",
|
||||||
|
projectId: "master-agent",
|
||||||
|
account: "17600003315",
|
||||||
|
content: "当前主节点在线吗",
|
||||||
|
});
|
||||||
|
const proposals = await listMasterAgentEvolutionProposals();
|
||||||
|
assert.equal(proposals[0]?.proposalType, "fast_path_rule");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行测试,确认失败**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-engine.test.ts`
|
||||||
|
Expected: FAIL,提示函数或 proposal 类型不存在
|
||||||
|
|
||||||
|
- [ ] **Step 3: 实现最小 engine API**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export async function recordMasterAgentEvolutionSignal(...) {}
|
||||||
|
export async function listMasterAgentEvolutionProposals(...) {}
|
||||||
|
export async function applyMasterAgentEvolutionProposal(...) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 实现第一批 proposal 生成规则**
|
||||||
|
|
||||||
|
规则最小集:
|
||||||
|
- repeated deterministic questions -> `fast_path_rule`
|
||||||
|
- repeated backend/model corrections -> `routing_preference_patch`
|
||||||
|
- repeated user preference statements -> `memory_patch`
|
||||||
|
|
||||||
|
- [ ] **Step 5: 运行测试确认通过**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-engine.test.ts`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 6: 提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add src/lib/master-agent-evolution.ts tests/master-agent-evolution-engine.test.ts
|
||||||
|
git commit -m "feat: add master agent evolution engine"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 3: 把 Evolution Engine 挂到主Agent主链
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/src/lib/boss-master-agent.ts`
|
||||||
|
- Test: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/tests/master-agent-evolution-integration.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写失败测试,验证快路径未命中后会记录 signal**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
test("master agent slow deterministic query records evolution signal when fast path misses", async () => {
|
||||||
|
await replyToMasterAgentUserMessage({
|
||||||
|
requestText: "当前线程是不是正在运行",
|
||||||
|
requestedBy: "Boss 超级管理员",
|
||||||
|
requestedByAccount: "17600003315",
|
||||||
|
mode: "enqueue",
|
||||||
|
});
|
||||||
|
const signals = await listMasterAgentEvolutionSignals();
|
||||||
|
assert.ok(signals.some((item) => item.kind === "fast_path_candidate"));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行测试,确认失败**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-integration.test.ts`
|
||||||
|
Expected: FAIL
|
||||||
|
|
||||||
|
- [ ] **Step 3: 在 `replyToMasterAgentUserMessage()` 和相关成功/失败路径中记录 signal**
|
||||||
|
|
||||||
|
最小挂载点:
|
||||||
|
- Fast Path 命中
|
||||||
|
- Fast Path 未命中但命中确定性查询模式
|
||||||
|
- 后端 fallback
|
||||||
|
- 用户手动切模型/切后端/切接管
|
||||||
|
|
||||||
|
- [ ] **Step 4: 运行测试确认通过**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-integration.test.ts`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 5: 提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add src/lib/boss-master-agent.ts tests/master-agent-evolution-integration.test.ts
|
||||||
|
git commit -m "feat: integrate evolution engine into master agent"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 4: 实现 Evolution API
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/src/app/api/v1/master-agent/evolution/route.ts`
|
||||||
|
- Create: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/src/app/api/v1/master-agent/evolution/config/route.ts`
|
||||||
|
- Create: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/src/app/api/v1/master-agent/evolution/proposals/[proposalId]/approve/route.ts`
|
||||||
|
- Create: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/src/app/api/v1/master-agent/evolution/proposals/[proposalId]/reject/route.ts`
|
||||||
|
- Test: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/tests/master-agent-evolution-routes.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写失败测试,定义 GET/POST contract**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
test("GET /api/v1/master-agent/evolution returns config proposals and rules", async () => {
|
||||||
|
const response = await GET(request);
|
||||||
|
const payload = await response.json();
|
||||||
|
assert.equal(payload.ok, true);
|
||||||
|
assert.ok(Array.isArray(payload.proposals));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行测试,确认失败**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-routes.test.ts`
|
||||||
|
Expected: FAIL
|
||||||
|
|
||||||
|
- [ ] **Step 3: 实现读接口和 config 切换接口**
|
||||||
|
|
||||||
|
- [ ] **Step 4: 实现 approve / reject 接口**
|
||||||
|
|
||||||
|
- [ ] **Step 5: 运行测试确认通过**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-routes.test.ts`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 6: 提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add src/app/api/v1/master-agent/evolution src/app/api/v1/master-agent/evolution/config src/app/api/v1/master-agent/evolution/proposals tests/master-agent-evolution-routes.test.ts
|
||||||
|
git commit -m "feat: add master agent evolution routes"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 5: 加入 Controlled / Autonomous 自动采纳分界
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/src/lib/master-agent-evolution.ts`
|
||||||
|
- Test: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/tests/master-agent-evolution-autonomous.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写失败测试,验证 controlled 不自动采纳、autonomous 可自动采纳低风险 proposal**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
test("controlled mode keeps proposal pending_review", async () => {
|
||||||
|
await setMasterAgentEvolutionMode("controlled");
|
||||||
|
// record signal...
|
||||||
|
assert.equal(proposals[0]?.status, "pending_review");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("autonomous mode auto applies low risk routing preference patch", async () => {
|
||||||
|
await setMasterAgentEvolutionMode("autonomous");
|
||||||
|
// record signal...
|
||||||
|
assert.equal(proposals[0]?.status, "auto_applied");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行测试,确认失败**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-autonomous.test.ts`
|
||||||
|
Expected: FAIL
|
||||||
|
|
||||||
|
- [ ] **Step 3: 实现 mode gating 与低风险自动采纳白名单**
|
||||||
|
|
||||||
|
白名单:
|
||||||
|
- `memory_patch`
|
||||||
|
- `routing_preference_patch`
|
||||||
|
- `fast_path_rule`
|
||||||
|
|
||||||
|
黑名单:
|
||||||
|
- 管理员全局主提示词修改
|
||||||
|
- 高风险提示词覆盖
|
||||||
|
|
||||||
|
- [ ] **Step 4: 运行测试确认通过**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution-autonomous.test.ts`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 5: 提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add src/lib/master-agent-evolution.ts tests/master-agent-evolution-autonomous.test.ts
|
||||||
|
git commit -m "feat: add controlled and autonomous evolution modes"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 6: 更新交接文档并切双分支
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/docs/architecture/ai_handoff_index_cn.md`
|
||||||
|
- Modify: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/docs/architecture/current_runtime_and_deploy_status_cn.md`
|
||||||
|
- Modify: `/Users/kris/.config/superpowers/worktrees/boss/codex-hermes-backend-mvp/docs/superpowers/specs/2026-04-16-master-agent-evolution-engine-design.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 文档中加入 evolution engine 与两种模式说明**
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行组合验证**
|
||||||
|
|
||||||
|
Run: `npx tsx --test tests/master-agent-evolution*.test.ts tests/master-agent-message-queue.test.ts tests/master-agent-chat-controls.test.ts`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
Run: `npm run build`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 3: 提交共享内核**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add docs/architecture/ai_handoff_index_cn.md docs/architecture/current_runtime_and_deploy_status_cn.md docs/superpowers/specs/2026-04-16-master-agent-evolution-engine-design.md
|
||||||
|
git commit -m "docs: document master agent evolution engine"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 创建 controlled 分支并推送**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git branch codex/master-agent-controlled-evolution
|
||||||
|
git push gitea codex/master-agent-controlled-evolution
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 创建 autonomous 分支,在其上把默认 mode 切到 autonomous 并推送**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout -b codex/master-agent-autonomous-evolution
|
||||||
|
git push gitea codex/master-agent-autonomous-evolution
|
||||||
|
```
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
# 主Agent 自动进化引擎设计
|
||||||
|
|
||||||
|
更新时间:`2026-04-16`
|
||||||
|
|
||||||
|
## 1. 背景
|
||||||
|
|
||||||
|
当前 Boss 主Agent 已经具备:
|
||||||
|
|
||||||
|
- 长期记忆:`masterAgentMemories`
|
||||||
|
- 提示词策略:`masterAgentPromptPolicy`、`userMasterPrompts`
|
||||||
|
- 对话控制:`userProjectAgentControls`
|
||||||
|
- 运行时自适应:Master Node / Hermes / Claw / API fallback
|
||||||
|
|
||||||
|
但它还不具备真正的自动进化闭环。现状只能“记住”与“回退”,不能持续发现问题、形成提案、沉淀规则并在后续回复中生效。
|
||||||
|
|
||||||
|
## 2. 目标
|
||||||
|
|
||||||
|
- 给主Agent增加统一的 `Evolution Engine`
|
||||||
|
- 支持两种能力范围:
|
||||||
|
- `controlled`:受控自动进化,只生成提案,等待用户批准后落地
|
||||||
|
- `autonomous`:完全自我进化,对高置信度提案自动落地
|
||||||
|
- 进化逻辑对现有主链最小侵入,不重写现有回复链、任务链、记忆链
|
||||||
|
- 所有进化行为可追踪、可审计、可回滚
|
||||||
|
|
||||||
|
## 3. 核心概念
|
||||||
|
|
||||||
|
### 3.1 Evolution Signal
|
||||||
|
|
||||||
|
进化信号,记录“为什么应该考虑进化”。
|
||||||
|
|
||||||
|
第一批信号来源:
|
||||||
|
|
||||||
|
- 高频重复问句
|
||||||
|
- 慢回复问题
|
||||||
|
- 用户纠正主Agent
|
||||||
|
- 模型/后端频繁手动切换
|
||||||
|
- 后端回退或执行失败
|
||||||
|
- Fast Path 未命中但属于确定性状态问题
|
||||||
|
|
||||||
|
### 3.2 Evolution Proposal
|
||||||
|
|
||||||
|
进化提案,记录“建议怎么改”。
|
||||||
|
|
||||||
|
第一批提案类型:
|
||||||
|
|
||||||
|
- `fast_path_rule`
|
||||||
|
- `prompt_policy_patch`
|
||||||
|
- `memory_patch`
|
||||||
|
- `agent_control_patch`
|
||||||
|
- `routing_preference_patch`
|
||||||
|
|
||||||
|
### 3.3 Evolution Rule
|
||||||
|
|
||||||
|
已生效规则,表示被批准或自动采纳后真正进入系统长期状态的变更。
|
||||||
|
|
||||||
|
### 3.4 Evolution Run Log
|
||||||
|
|
||||||
|
进化执行日志,记录某次信号分析、提案生成、采纳、拒绝、自动落地的全过程。
|
||||||
|
|
||||||
|
## 4. 运行模式
|
||||||
|
|
||||||
|
### 4.1 Controlled
|
||||||
|
|
||||||
|
受控自动进化:
|
||||||
|
|
||||||
|
- 主Agent可以自动记录 signal
|
||||||
|
- 可以自动生成 proposal
|
||||||
|
- 不允许自动改长期状态
|
||||||
|
- 只能在用户批准后写入 memory / prompt / controls / fast-path rules
|
||||||
|
|
||||||
|
适合生产稳态与审计要求更高的环境。
|
||||||
|
|
||||||
|
### 4.2 Autonomous
|
||||||
|
|
||||||
|
完全自我进化:
|
||||||
|
|
||||||
|
- 主Agent可以自动记录 signal
|
||||||
|
- 自动生成 proposal
|
||||||
|
- 对满足阈值的 proposal 直接自动采纳
|
||||||
|
- 自动把结果写入长期状态
|
||||||
|
- 仍保留完整日志,允许后续回滚
|
||||||
|
|
||||||
|
适合开发期快速迭代,用更高风险换更快自优化速度。
|
||||||
|
|
||||||
|
## 5. 架构
|
||||||
|
|
||||||
|
### 5.1 新增状态模型
|
||||||
|
|
||||||
|
在 `BossState` 中新增:
|
||||||
|
|
||||||
|
- `masterAgentEvolutionConfig`
|
||||||
|
- `masterAgentEvolutionSignals`
|
||||||
|
- `masterAgentEvolutionProposals`
|
||||||
|
- `masterAgentEvolutionRules`
|
||||||
|
- `masterAgentEvolutionRunLogs`
|
||||||
|
|
||||||
|
### 5.2 新增引擎文件
|
||||||
|
|
||||||
|
- `src/lib/master-agent-evolution.ts`
|
||||||
|
- 统一入口
|
||||||
|
- signal 归一化
|
||||||
|
- proposal 生成
|
||||||
|
- 自动采纳判断
|
||||||
|
- 规则落地
|
||||||
|
- `src/lib/master-agent-evolution-rules.ts`
|
||||||
|
- Fast Path 规则、路由偏好规则解析
|
||||||
|
- `src/lib/master-agent-evolution-projections.ts`
|
||||||
|
- 给 Web/API 返回 evolution 状态摘要
|
||||||
|
|
||||||
|
### 5.3 挂载点
|
||||||
|
|
||||||
|
第一批最小侵入挂载点:
|
||||||
|
|
||||||
|
- `replyToMasterAgentUserMessage()`
|
||||||
|
- 记录用户问题、Fast Path 命中/未命中、慢路径耗时、模型/后端实际选择
|
||||||
|
- `appendMasterAgentSystemReply()`
|
||||||
|
- 记录主Agent输出结果
|
||||||
|
- `completeMasterAgentTask()`
|
||||||
|
- 记录异步任务成功/失败、fallback、超时
|
||||||
|
- `updateProjectAgentControls()` / `updateMasterAgentPromptPolicy()` / `createUserMasterMemory()`
|
||||||
|
- 记录用户主动纠偏行为,作为 evolution signal 输入
|
||||||
|
|
||||||
|
## 6. 第一批自动进化能力
|
||||||
|
|
||||||
|
### Controlled 与 Autonomous 共用
|
||||||
|
|
||||||
|
- 记录重复问句
|
||||||
|
- 记录低价值慢路径
|
||||||
|
- 记录用户纠正模型/后端/接管设置
|
||||||
|
- 自动生成三类提案:
|
||||||
|
- 新增 Fast Path 规则
|
||||||
|
- 调整默认快模型/强模型/后端偏好
|
||||||
|
- 追加 workflow_rule / user_preference 记忆
|
||||||
|
|
||||||
|
### Controlled 专属行为
|
||||||
|
|
||||||
|
- 所有提案状态默认为 `pending_review`
|
||||||
|
- 需要用户批准后才能落地
|
||||||
|
|
||||||
|
### Autonomous 专属行为
|
||||||
|
|
||||||
|
- 当 proposal 满足“高置信度 + 低破坏性”时自动落地
|
||||||
|
- 第一批只允许自动落地:
|
||||||
|
- `memory_patch`
|
||||||
|
- `routing_preference_patch`
|
||||||
|
- `fast_path_rule`
|
||||||
|
- 不允许自动改管理员全局主提示词
|
||||||
|
|
||||||
|
## 7. 前台入口
|
||||||
|
|
||||||
|
第一批只做最小 Web 能力:
|
||||||
|
|
||||||
|
- `GET /api/v1/master-agent/evolution`
|
||||||
|
- 返回 config / pending proposals / recent run logs / effective rules
|
||||||
|
- `POST /api/v1/master-agent/evolution/config`
|
||||||
|
- 切换 `controlled` / `autonomous`
|
||||||
|
- `POST /api/v1/master-agent/evolution/proposals/[proposalId]/approve`
|
||||||
|
- `POST /api/v1/master-agent/evolution/proposals/[proposalId]/reject`
|
||||||
|
|
||||||
|
先不做复杂 UI,先把数据和接口打通,必要时从 `我的 > 主Agent` 进入。
|
||||||
|
|
||||||
|
## 8. 双分支策略
|
||||||
|
|
||||||
|
统一内核先落在共享提交里:
|
||||||
|
|
||||||
|
- evolution state
|
||||||
|
- evolution engine
|
||||||
|
- proposal / rule / run-log 结构
|
||||||
|
- API 与测试
|
||||||
|
|
||||||
|
然后从共享基线分两条分支:
|
||||||
|
|
||||||
|
- `codex/master-agent-controlled-evolution`
|
||||||
|
- 默认 `mode=controlled`
|
||||||
|
- 关闭自动采纳
|
||||||
|
- `codex/master-agent-autonomous-evolution`
|
||||||
|
- 默认 `mode=autonomous`
|
||||||
|
- 开启自动采纳阈值逻辑
|
||||||
|
|
||||||
|
用户先使用 autonomous 分支验证真实开发效率;一旦发现策略漂移或错误写入,可直接回退 controlled 分支。
|
||||||
|
|
||||||
|
## 9. 风险控制
|
||||||
|
|
||||||
|
- 管理员全局主提示词永远不允许 autonomous 自动改写
|
||||||
|
- 自动落地范围先限制在低风险对象
|
||||||
|
- 每次自动采纳都必须写 run log
|
||||||
|
- 所有 rule 都要带 `sourceProposalId`
|
||||||
|
- 所有 proposal 都要可拒绝、可归档、可回滚
|
||||||
|
|
||||||
|
## 10. 验证基线
|
||||||
|
|
||||||
|
- `npx tsx --test tests/master-agent-evolution*.test.ts`
|
||||||
|
- `npx tsx --test tests/master-agent-message-queue.test.ts tests/master-agent-chat-controls.test.ts`
|
||||||
|
- `npm run build`
|
||||||
|
- 线上切到 `autonomous` 后:
|
||||||
|
- 主Agent可记录进化信号
|
||||||
|
- 可自动生成并落地低风险规则
|
||||||
|
- `controlled` 分支同一套信号与提案只记录、不自动采纳
|
||||||
44
src/app/api/v1/master-agent/evolution/config/route.ts
Normal file
44
src/app/api/v1/master-agent/evolution/config/route.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { requireRequestSession } from "@/lib/boss-auth";
|
||||||
|
import { jsonNoStore } from "@/lib/api-response";
|
||||||
|
import { setMasterAgentEvolutionMode } from "@/lib/master-agent-evolution";
|
||||||
|
|
||||||
|
export const runtime = "nodejs";
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
const session = await requireRequestSession(request);
|
||||||
|
if (!session) {
|
||||||
|
return jsonNoStore({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||||
|
}
|
||||||
|
return jsonNoStore({ ok: false, message: "METHOD_NOT_ALLOWED" }, { status: 405 });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(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 rawBody = await request.text().catch(() => "");
|
||||||
|
let body: unknown;
|
||||||
|
try {
|
||||||
|
body = JSON.parse(rawBody);
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ ok: false, message: "INVALID_JSON_PAYLOAD" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
||||||
|
return NextResponse.json({ ok: false, message: "INVALID_EVOLUTION_CONFIG_PAYLOAD" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = body as { mode?: unknown };
|
||||||
|
if (payload.mode !== "controlled" && payload.mode !== "autonomous") {
|
||||||
|
return NextResponse.json({ ok: false, message: "INVALID_EVOLUTION_MODE" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await setMasterAgentEvolutionMode(payload.mode);
|
||||||
|
return NextResponse.json({ ok: true, config });
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { requireRequestSession } from "@/lib/boss-auth";
|
||||||
|
import { approveMasterAgentEvolutionProposal } from "@/lib/master-agent-evolution";
|
||||||
|
|
||||||
|
export const runtime = "nodejs";
|
||||||
|
|
||||||
|
export async function POST(
|
||||||
|
request: NextRequest,
|
||||||
|
context: { params: Promise<{ proposalId: string }> },
|
||||||
|
) {
|
||||||
|
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 { proposalId } = await context.params;
|
||||||
|
try {
|
||||||
|
const proposal = await approveMasterAgentEvolutionProposal(proposalId, session.account);
|
||||||
|
return NextResponse.json({ ok: true, proposal });
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ ok: false, message: error instanceof Error ? error.message : "UNKNOWN_ERROR" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { requireRequestSession } from "@/lib/boss-auth";
|
||||||
|
import { rejectMasterAgentEvolutionProposal } from "@/lib/master-agent-evolution";
|
||||||
|
|
||||||
|
export const runtime = "nodejs";
|
||||||
|
|
||||||
|
export async function POST(
|
||||||
|
request: NextRequest,
|
||||||
|
context: { params: Promise<{ proposalId: string }> },
|
||||||
|
) {
|
||||||
|
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 { proposalId } = await context.params;
|
||||||
|
try {
|
||||||
|
const proposal = await rejectMasterAgentEvolutionProposal(proposalId, session.account);
|
||||||
|
return NextResponse.json({ ok: true, proposal });
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ ok: false, message: error instanceof Error ? error.message : "UNKNOWN_ERROR" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/app/api/v1/master-agent/evolution/route.ts
Normal file
16
src/app/api/v1/master-agent/evolution/route.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { NextRequest } from "next/server";
|
||||||
|
import { requireRequestSession } from "@/lib/boss-auth";
|
||||||
|
import { jsonNoStore } from "@/lib/api-response";
|
||||||
|
import { getMasterAgentEvolutionDashboard } from "@/lib/master-agent-evolution";
|
||||||
|
|
||||||
|
export const runtime = "nodejs";
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
const session = await requireRequestSession(request);
|
||||||
|
if (!session) {
|
||||||
|
return jsonNoStore({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const dashboard = await getMasterAgentEvolutionDashboard();
|
||||||
|
return jsonNoStore({ ok: true, ...dashboard });
|
||||||
|
}
|
||||||
@@ -270,6 +270,86 @@ export interface MasterAgentMemory {
|
|||||||
archived: boolean;
|
archived: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MasterAgentEvolutionMode = "controlled" | "autonomous";
|
||||||
|
export type MasterAgentEvolutionSignalKind =
|
||||||
|
| "repeated_question"
|
||||||
|
| "fast_path_candidate"
|
||||||
|
| "slow_path"
|
||||||
|
| "user_correction"
|
||||||
|
| "backend_fallback";
|
||||||
|
export type MasterAgentEvolutionProposalType =
|
||||||
|
| "fast_path_rule"
|
||||||
|
| "prompt_policy_patch"
|
||||||
|
| "memory_patch"
|
||||||
|
| "agent_control_patch"
|
||||||
|
| "routing_preference_patch";
|
||||||
|
export type MasterAgentEvolutionProposalStatus =
|
||||||
|
| "pending_review"
|
||||||
|
| "approved"
|
||||||
|
| "rejected"
|
||||||
|
| "auto_applied";
|
||||||
|
export type MasterAgentEvolutionRunAction =
|
||||||
|
| "signal_recorded"
|
||||||
|
| "proposal_created"
|
||||||
|
| "proposal_approved"
|
||||||
|
| "proposal_rejected"
|
||||||
|
| "proposal_auto_applied";
|
||||||
|
|
||||||
|
export interface MasterAgentEvolutionConfig {
|
||||||
|
mode: MasterAgentEvolutionMode;
|
||||||
|
autoApplyLowRiskRules: boolean;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MasterAgentEvolutionSignal {
|
||||||
|
signalId: string;
|
||||||
|
kind: MasterAgentEvolutionSignalKind;
|
||||||
|
account: string;
|
||||||
|
projectId: string;
|
||||||
|
requestText: string;
|
||||||
|
replyText?: string;
|
||||||
|
metadataJson?: Record<string, unknown>;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MasterAgentEvolutionProposal {
|
||||||
|
proposalId: string;
|
||||||
|
proposalType: MasterAgentEvolutionProposalType;
|
||||||
|
status: MasterAgentEvolutionProposalStatus;
|
||||||
|
account: string;
|
||||||
|
projectId: string;
|
||||||
|
title: string;
|
||||||
|
summary: string;
|
||||||
|
patchJson: Record<string, unknown>;
|
||||||
|
sourceSignalIds: string[];
|
||||||
|
confidence: number;
|
||||||
|
riskLevel: "low" | "medium" | "high";
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MasterAgentEvolutionRule {
|
||||||
|
ruleId: string;
|
||||||
|
ruleType: MasterAgentEvolutionProposalType;
|
||||||
|
account: string;
|
||||||
|
projectId?: string;
|
||||||
|
sourceProposalId: string;
|
||||||
|
patchJson: Record<string, unknown>;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MasterAgentEvolutionRunLog {
|
||||||
|
runId: string;
|
||||||
|
action: MasterAgentEvolutionRunAction;
|
||||||
|
account: string;
|
||||||
|
projectId: string;
|
||||||
|
signalId?: string;
|
||||||
|
proposalId?: string;
|
||||||
|
note: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GoalItem {
|
export interface GoalItem {
|
||||||
id: string;
|
id: string;
|
||||||
text: string;
|
text: string;
|
||||||
@@ -1078,6 +1158,11 @@ export interface BossState {
|
|||||||
masterAgentPromptPolicy: MasterAgentPromptPolicy | null;
|
masterAgentPromptPolicy: MasterAgentPromptPolicy | null;
|
||||||
userMasterPrompts: UserMasterPrompt[];
|
userMasterPrompts: UserMasterPrompt[];
|
||||||
masterAgentMemories: MasterAgentMemory[];
|
masterAgentMemories: MasterAgentMemory[];
|
||||||
|
masterAgentEvolutionConfig: MasterAgentEvolutionConfig;
|
||||||
|
masterAgentEvolutionSignals: MasterAgentEvolutionSignal[];
|
||||||
|
masterAgentEvolutionProposals: MasterAgentEvolutionProposal[];
|
||||||
|
masterAgentEvolutionRules: MasterAgentEvolutionRule[];
|
||||||
|
masterAgentEvolutionRunLogs: MasterAgentEvolutionRunLog[];
|
||||||
userProjectAgentControls: UserProjectAgentControls[];
|
userProjectAgentControls: UserProjectAgentControls[];
|
||||||
threadContextSnapshots: ThreadContextSnapshot[];
|
threadContextSnapshots: ThreadContextSnapshot[];
|
||||||
threadHandoffPackages: ThreadHandoffPackage[];
|
threadHandoffPackages: ThreadHandoffPackage[];
|
||||||
@@ -1451,6 +1536,15 @@ const initialState: BossState = {
|
|||||||
masterAgentPromptPolicy: null,
|
masterAgentPromptPolicy: null,
|
||||||
userMasterPrompts: [],
|
userMasterPrompts: [],
|
||||||
masterAgentMemories: [],
|
masterAgentMemories: [],
|
||||||
|
masterAgentEvolutionConfig: {
|
||||||
|
mode: "controlled",
|
||||||
|
autoApplyLowRiskRules: false,
|
||||||
|
updatedAt: nowIso(),
|
||||||
|
},
|
||||||
|
masterAgentEvolutionSignals: [],
|
||||||
|
masterAgentEvolutionProposals: [],
|
||||||
|
masterAgentEvolutionRules: [],
|
||||||
|
masterAgentEvolutionRunLogs: [],
|
||||||
userProjectAgentControls: [],
|
userProjectAgentControls: [],
|
||||||
masterAgentTasks: [],
|
masterAgentTasks: [],
|
||||||
dispatchPlans: [],
|
dispatchPlans: [],
|
||||||
@@ -2967,6 +3061,101 @@ function normalizeUserMasterMemory(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeMasterAgentEvolutionConfig(
|
||||||
|
raw: Partial<MasterAgentEvolutionConfig> | null | undefined,
|
||||||
|
fallback?: MasterAgentEvolutionConfig,
|
||||||
|
): MasterAgentEvolutionConfig {
|
||||||
|
return {
|
||||||
|
mode: raw?.mode === "autonomous" ? "autonomous" : fallback?.mode ?? "controlled",
|
||||||
|
autoApplyLowRiskRules:
|
||||||
|
typeof raw?.autoApplyLowRiskRules === "boolean"
|
||||||
|
? raw.autoApplyLowRiskRules
|
||||||
|
: fallback?.autoApplyLowRiskRules ?? false,
|
||||||
|
updatedAt: raw?.updatedAt ?? fallback?.updatedAt ?? nowIso(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeMasterAgentEvolutionSignal(
|
||||||
|
raw: Partial<MasterAgentEvolutionSignal>,
|
||||||
|
fallback?: MasterAgentEvolutionSignal,
|
||||||
|
): MasterAgentEvolutionSignal {
|
||||||
|
return {
|
||||||
|
signalId: raw.signalId ?? fallback?.signalId ?? randomToken("evo-signal"),
|
||||||
|
kind: raw.kind ?? fallback?.kind ?? "slow_path",
|
||||||
|
account: trimToDefined(raw.account ?? fallback?.account) ?? "",
|
||||||
|
projectId: trimToDefined(raw.projectId ?? fallback?.projectId) ?? "master-agent",
|
||||||
|
requestText: trimToDefined(raw.requestText ?? fallback?.requestText) ?? "",
|
||||||
|
replyText: trimToDefined(raw.replyText ?? fallback?.replyText),
|
||||||
|
metadataJson:
|
||||||
|
raw.metadataJson && typeof raw.metadataJson === "object"
|
||||||
|
? raw.metadataJson
|
||||||
|
: fallback?.metadataJson ?? {},
|
||||||
|
createdAt: raw.createdAt ?? fallback?.createdAt ?? nowIso(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeMasterAgentEvolutionProposal(
|
||||||
|
raw: Partial<MasterAgentEvolutionProposal>,
|
||||||
|
fallback?: MasterAgentEvolutionProposal,
|
||||||
|
): MasterAgentEvolutionProposal {
|
||||||
|
return {
|
||||||
|
proposalId: raw.proposalId ?? fallback?.proposalId ?? randomToken("evo-proposal"),
|
||||||
|
proposalType: raw.proposalType ?? fallback?.proposalType ?? "memory_patch",
|
||||||
|
status: raw.status ?? fallback?.status ?? "pending_review",
|
||||||
|
account: trimToDefined(raw.account ?? fallback?.account) ?? "",
|
||||||
|
projectId: trimToDefined(raw.projectId ?? fallback?.projectId) ?? "master-agent",
|
||||||
|
title: trimToDefined(raw.title ?? fallback?.title) ?? "进化提案",
|
||||||
|
summary: trimToDefined(raw.summary ?? fallback?.summary) ?? "",
|
||||||
|
patchJson:
|
||||||
|
raw.patchJson && typeof raw.patchJson === "object"
|
||||||
|
? raw.patchJson
|
||||||
|
: fallback?.patchJson ?? {},
|
||||||
|
sourceSignalIds: dedupeStrings(
|
||||||
|
ensureArray(raw.sourceSignalIds, fallback?.sourceSignalIds ?? []).map((value) => value.trim()),
|
||||||
|
),
|
||||||
|
confidence:
|
||||||
|
typeof raw.confidence === "number" ? raw.confidence : fallback?.confidence ?? 0.5,
|
||||||
|
riskLevel: raw.riskLevel ?? fallback?.riskLevel ?? "low",
|
||||||
|
createdAt: raw.createdAt ?? fallback?.createdAt ?? nowIso(),
|
||||||
|
updatedAt: raw.updatedAt ?? fallback?.updatedAt ?? nowIso(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeMasterAgentEvolutionRule(
|
||||||
|
raw: Partial<MasterAgentEvolutionRule>,
|
||||||
|
fallback?: MasterAgentEvolutionRule,
|
||||||
|
): MasterAgentEvolutionRule {
|
||||||
|
return {
|
||||||
|
ruleId: raw.ruleId ?? fallback?.ruleId ?? randomToken("evo-rule"),
|
||||||
|
ruleType: raw.ruleType ?? fallback?.ruleType ?? "memory_patch",
|
||||||
|
account: trimToDefined(raw.account ?? fallback?.account) ?? "",
|
||||||
|
projectId: trimToDefined(raw.projectId ?? fallback?.projectId),
|
||||||
|
sourceProposalId: trimToDefined(raw.sourceProposalId ?? fallback?.sourceProposalId) ?? "",
|
||||||
|
patchJson:
|
||||||
|
raw.patchJson && typeof raw.patchJson === "object"
|
||||||
|
? raw.patchJson
|
||||||
|
: fallback?.patchJson ?? {},
|
||||||
|
createdAt: raw.createdAt ?? fallback?.createdAt ?? nowIso(),
|
||||||
|
updatedAt: raw.updatedAt ?? fallback?.updatedAt ?? nowIso(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeMasterAgentEvolutionRunLog(
|
||||||
|
raw: Partial<MasterAgentEvolutionRunLog>,
|
||||||
|
fallback?: MasterAgentEvolutionRunLog,
|
||||||
|
): MasterAgentEvolutionRunLog {
|
||||||
|
return {
|
||||||
|
runId: raw.runId ?? fallback?.runId ?? randomToken("evo-run"),
|
||||||
|
action: raw.action ?? fallback?.action ?? "signal_recorded",
|
||||||
|
account: trimToDefined(raw.account ?? fallback?.account) ?? "",
|
||||||
|
projectId: trimToDefined(raw.projectId ?? fallback?.projectId) ?? "master-agent",
|
||||||
|
signalId: trimToDefined(raw.signalId ?? fallback?.signalId),
|
||||||
|
proposalId: trimToDefined(raw.proposalId ?? fallback?.proposalId),
|
||||||
|
note: trimToDefined(raw.note ?? fallback?.note) ?? "",
|
||||||
|
createdAt: raw.createdAt ?? fallback?.createdAt ?? nowIso(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeProject(raw: Partial<Project>, fallback?: Project): Project {
|
function normalizeProject(raw: Partial<Project>, fallback?: Project): Project {
|
||||||
const base = fallback ?? cloneInitialState().projects[0];
|
const base = fallback ?? cloneInitialState().projects[0];
|
||||||
const projectId = raw.id ?? base.id;
|
const projectId = raw.id ?? base.id;
|
||||||
@@ -3495,6 +3684,46 @@ function normalizeState(raw: Partial<BossState> | undefined): BossState {
|
|||||||
base.masterAgentMemories[index % Math.max(1, base.masterAgentMemories.length)],
|
base.masterAgentMemories[index % Math.max(1, base.masterAgentMemories.length)],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
masterAgentEvolutionConfig: normalizeMasterAgentEvolutionConfig(
|
||||||
|
raw.masterAgentEvolutionConfig,
|
||||||
|
base.masterAgentEvolutionConfig,
|
||||||
|
),
|
||||||
|
masterAgentEvolutionSignals: ensureArray(
|
||||||
|
raw.masterAgentEvolutionSignals,
|
||||||
|
base.masterAgentEvolutionSignals,
|
||||||
|
).map((signal, index) =>
|
||||||
|
normalizeMasterAgentEvolutionSignal(
|
||||||
|
signal,
|
||||||
|
base.masterAgentEvolutionSignals[index % Math.max(1, base.masterAgentEvolutionSignals.length)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
masterAgentEvolutionProposals: ensureArray(
|
||||||
|
raw.masterAgentEvolutionProposals,
|
||||||
|
base.masterAgentEvolutionProposals,
|
||||||
|
).map((proposal, index) =>
|
||||||
|
normalizeMasterAgentEvolutionProposal(
|
||||||
|
proposal,
|
||||||
|
base.masterAgentEvolutionProposals[index % Math.max(1, base.masterAgentEvolutionProposals.length)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
masterAgentEvolutionRules: ensureArray(
|
||||||
|
raw.masterAgentEvolutionRules,
|
||||||
|
base.masterAgentEvolutionRules,
|
||||||
|
).map((rule, index) =>
|
||||||
|
normalizeMasterAgentEvolutionRule(
|
||||||
|
rule,
|
||||||
|
base.masterAgentEvolutionRules[index % Math.max(1, base.masterAgentEvolutionRules.length)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
masterAgentEvolutionRunLogs: ensureArray(
|
||||||
|
raw.masterAgentEvolutionRunLogs,
|
||||||
|
base.masterAgentEvolutionRunLogs,
|
||||||
|
).map((log, index) =>
|
||||||
|
normalizeMasterAgentEvolutionRunLog(
|
||||||
|
log,
|
||||||
|
base.masterAgentEvolutionRunLogs[index % Math.max(1, base.masterAgentEvolutionRunLogs.length)],
|
||||||
|
),
|
||||||
|
),
|
||||||
userProjectAgentControls: ensureArray(
|
userProjectAgentControls: ensureArray(
|
||||||
raw.userProjectAgentControls,
|
raw.userProjectAgentControls,
|
||||||
base.userProjectAgentControls,
|
base.userProjectAgentControls,
|
||||||
@@ -4991,6 +5220,191 @@ export async function touchUserMasterMemories(memoryIds: string[], account: stri
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMasterAgentEvolutionState() {
|
||||||
|
const state = await readState();
|
||||||
|
return {
|
||||||
|
config: state.masterAgentEvolutionConfig,
|
||||||
|
signals: [...state.masterAgentEvolutionSignals],
|
||||||
|
proposals: [...state.masterAgentEvolutionProposals],
|
||||||
|
rules: [...state.masterAgentEvolutionRules],
|
||||||
|
runLogs: [...state.masterAgentEvolutionRunLogs],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateMasterAgentEvolutionConfig(input: {
|
||||||
|
mode?: MasterAgentEvolutionMode;
|
||||||
|
autoApplyLowRiskRules?: boolean;
|
||||||
|
}) {
|
||||||
|
const result = await mutateState((state) => {
|
||||||
|
const current = state.masterAgentEvolutionConfig;
|
||||||
|
const next = normalizeMasterAgentEvolutionConfig({
|
||||||
|
mode: input.mode ?? current.mode,
|
||||||
|
autoApplyLowRiskRules:
|
||||||
|
typeof input.autoApplyLowRiskRules === "boolean"
|
||||||
|
? input.autoApplyLowRiskRules
|
||||||
|
: current.autoApplyLowRiskRules,
|
||||||
|
updatedAt: nowIso(),
|
||||||
|
}, current);
|
||||||
|
if (next.mode === "controlled") {
|
||||||
|
next.autoApplyLowRiskRules = false;
|
||||||
|
}
|
||||||
|
if (next.mode === "autonomous" && input.autoApplyLowRiskRules === undefined) {
|
||||||
|
next.autoApplyLowRiskRules = true;
|
||||||
|
}
|
||||||
|
state.masterAgentEvolutionConfig = next;
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
publishBossEvent("master_agent.settings.updated", { projectId: "master-agent" });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function recordMasterAgentEvolutionSignalInState(input: {
|
||||||
|
kind: MasterAgentEvolutionSignalKind;
|
||||||
|
account: string;
|
||||||
|
projectId?: string;
|
||||||
|
requestText: string;
|
||||||
|
replyText?: string;
|
||||||
|
metadataJson?: Record<string, unknown>;
|
||||||
|
}) {
|
||||||
|
const result = await mutateState((state) => {
|
||||||
|
const signal = normalizeMasterAgentEvolutionSignal({
|
||||||
|
signalId: randomToken("evo-signal"),
|
||||||
|
kind: input.kind,
|
||||||
|
account: input.account,
|
||||||
|
projectId: input.projectId ?? "master-agent",
|
||||||
|
requestText: input.requestText,
|
||||||
|
replyText: input.replyText,
|
||||||
|
metadataJson: input.metadataJson ?? {},
|
||||||
|
createdAt: nowIso(),
|
||||||
|
});
|
||||||
|
state.masterAgentEvolutionSignals.unshift(signal);
|
||||||
|
state.masterAgentEvolutionRunLogs.unshift(
|
||||||
|
normalizeMasterAgentEvolutionRunLog({
|
||||||
|
runId: randomToken("evo-run"),
|
||||||
|
action: "signal_recorded",
|
||||||
|
account: signal.account,
|
||||||
|
projectId: signal.projectId,
|
||||||
|
signalId: signal.signalId,
|
||||||
|
note: `记录进化信号:${signal.kind}`,
|
||||||
|
createdAt: nowIso(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return signal;
|
||||||
|
});
|
||||||
|
publishBossEvent("master_agent.settings.updated", { projectId: "master-agent" });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createMasterAgentEvolutionProposalInState(input: {
|
||||||
|
proposalType: MasterAgentEvolutionProposalType;
|
||||||
|
account: string;
|
||||||
|
projectId?: string;
|
||||||
|
title: string;
|
||||||
|
summary: string;
|
||||||
|
patchJson: Record<string, unknown>;
|
||||||
|
sourceSignalIds: string[];
|
||||||
|
confidence: number;
|
||||||
|
riskLevel?: "low" | "medium" | "high";
|
||||||
|
status?: MasterAgentEvolutionProposalStatus;
|
||||||
|
}) {
|
||||||
|
const result = await mutateState((state) => {
|
||||||
|
const now = nowIso();
|
||||||
|
const proposal = normalizeMasterAgentEvolutionProposal({
|
||||||
|
proposalId: randomToken("evo-proposal"),
|
||||||
|
proposalType: input.proposalType,
|
||||||
|
status: input.status ?? "pending_review",
|
||||||
|
account: input.account,
|
||||||
|
projectId: input.projectId ?? "master-agent",
|
||||||
|
title: input.title,
|
||||||
|
summary: input.summary,
|
||||||
|
patchJson: input.patchJson,
|
||||||
|
sourceSignalIds: input.sourceSignalIds,
|
||||||
|
confidence: input.confidence,
|
||||||
|
riskLevel: input.riskLevel ?? "low",
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
});
|
||||||
|
state.masterAgentEvolutionProposals.unshift(proposal);
|
||||||
|
state.masterAgentEvolutionRunLogs.unshift(
|
||||||
|
normalizeMasterAgentEvolutionRunLog({
|
||||||
|
runId: randomToken("evo-run"),
|
||||||
|
action: proposal.status === "auto_applied" ? "proposal_auto_applied" : "proposal_created",
|
||||||
|
account: proposal.account,
|
||||||
|
projectId: proposal.projectId,
|
||||||
|
proposalId: proposal.proposalId,
|
||||||
|
note: proposal.title,
|
||||||
|
createdAt: now,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return proposal;
|
||||||
|
});
|
||||||
|
publishBossEvent("master_agent.settings.updated", { projectId: "master-agent" });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateMasterAgentEvolutionProposalStatus(input: {
|
||||||
|
proposalId: string;
|
||||||
|
status: MasterAgentEvolutionProposalStatus;
|
||||||
|
account: string;
|
||||||
|
note?: string;
|
||||||
|
}) {
|
||||||
|
const result = await mutateState((state) => {
|
||||||
|
const proposal = state.masterAgentEvolutionProposals.find((item) => item.proposalId === input.proposalId);
|
||||||
|
if (!proposal) {
|
||||||
|
throw new Error("MASTER_AGENT_EVOLUTION_PROPOSAL_NOT_FOUND");
|
||||||
|
}
|
||||||
|
proposal.status = input.status;
|
||||||
|
proposal.updatedAt = nowIso();
|
||||||
|
state.masterAgentEvolutionRunLogs.unshift(
|
||||||
|
normalizeMasterAgentEvolutionRunLog({
|
||||||
|
runId: randomToken("evo-run"),
|
||||||
|
action:
|
||||||
|
input.status === "approved"
|
||||||
|
? "proposal_approved"
|
||||||
|
: input.status === "rejected"
|
||||||
|
? "proposal_rejected"
|
||||||
|
: input.status === "auto_applied"
|
||||||
|
? "proposal_auto_applied"
|
||||||
|
: "proposal_created",
|
||||||
|
account: input.account,
|
||||||
|
projectId: proposal.projectId,
|
||||||
|
proposalId: proposal.proposalId,
|
||||||
|
note: input.note ?? proposal.title,
|
||||||
|
createdAt: nowIso(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return proposal;
|
||||||
|
});
|
||||||
|
publishBossEvent("master_agent.settings.updated", { projectId: "master-agent" });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createMasterAgentEvolutionRuleInState(input: {
|
||||||
|
ruleType: MasterAgentEvolutionProposalType;
|
||||||
|
account: string;
|
||||||
|
projectId?: string;
|
||||||
|
sourceProposalId: string;
|
||||||
|
patchJson: Record<string, unknown>;
|
||||||
|
}) {
|
||||||
|
const result = await mutateState((state) => {
|
||||||
|
const now = nowIso();
|
||||||
|
const rule = normalizeMasterAgentEvolutionRule({
|
||||||
|
ruleId: randomToken("evo-rule"),
|
||||||
|
ruleType: input.ruleType,
|
||||||
|
account: input.account,
|
||||||
|
projectId: input.projectId,
|
||||||
|
sourceProposalId: input.sourceProposalId,
|
||||||
|
patchJson: input.patchJson,
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
});
|
||||||
|
state.masterAgentEvolutionRules.unshift(rule);
|
||||||
|
return rule;
|
||||||
|
});
|
||||||
|
publishBossEvent("master_agent.settings.updated", { projectId: "master-agent" });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeAutoMemoryText(value: string | undefined) {
|
function normalizeAutoMemoryText(value: string | undefined) {
|
||||||
return (value ?? "")
|
return (value ?? "")
|
||||||
.replace(/\s+/g, " ")
|
.replace(/\s+/g, " ")
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import {
|
|||||||
listUserMasterMemoriesView,
|
listUserMasterMemoriesView,
|
||||||
} from "@/lib/boss-projections";
|
} from "@/lib/boss-projections";
|
||||||
import { normalizeRemoteExecutionResult } from "@/lib/execution/remote-runtime-adapter";
|
import { normalizeRemoteExecutionResult } from "@/lib/execution/remote-runtime-adapter";
|
||||||
|
import { recordMasterAgentEvolutionSignal } from "@/lib/master-agent-evolution";
|
||||||
|
|
||||||
type MasterAgentReplyState = "queued" | "running" | "completed";
|
type MasterAgentReplyState = "queued" | "running" | "completed";
|
||||||
type MasterAgentExecutionIntent = "chat" | "deep_task";
|
type MasterAgentExecutionIntent = "chat" | "deep_task";
|
||||||
@@ -1950,6 +1951,27 @@ async function appendFastPathError(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function tryRecordMasterAgentEvolutionSignal(input: {
|
||||||
|
kind: "fast_path_candidate" | "user_correction" | "backend_fallback";
|
||||||
|
account: string;
|
||||||
|
requestText: string;
|
||||||
|
replyText?: string;
|
||||||
|
metadataJson?: Record<string, unknown>;
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
await recordMasterAgentEvolutionSignal({
|
||||||
|
kind: input.kind,
|
||||||
|
account: input.account,
|
||||||
|
projectId: "master-agent",
|
||||||
|
requestText: input.requestText,
|
||||||
|
replyText: input.replyText,
|
||||||
|
metadataJson: input.metadataJson,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
// Evolution capture is best-effort and must not break replies.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function buildModelSummaryReply(context: MasterAgentFastIntentContext, requestText: string) {
|
function buildModelSummaryReply(context: MasterAgentFastIntentContext, requestText: string) {
|
||||||
const normalized = normalizeLexicalText(requestText);
|
const normalized = normalizeLexicalText(requestText);
|
||||||
const manualModel = context.agentControls?.modelOverride?.trim() || "";
|
const manualModel = context.agentControls?.modelOverride?.trim() || "";
|
||||||
@@ -2060,10 +2082,15 @@ async function tryHandleMasterAgentBackendSwitchCommand(params: {
|
|||||||
{ backendOverride: requestedBackend },
|
{ backendOverride: requestedBackend },
|
||||||
params.requestedByAccount,
|
params.requestedByAccount,
|
||||||
);
|
);
|
||||||
return appendFastPathReply(
|
const reply = `已把默认后端切到 ${requestedBackend}。`;
|
||||||
`已把默认后端切到 ${requestedBackend}。`,
|
await tryRecordMasterAgentEvolutionSignal({
|
||||||
buildMasterAgentModelSenderLabel(params.context.effectiveChatPolicy.model),
|
kind: "user_correction",
|
||||||
);
|
account: params.requestedByAccount,
|
||||||
|
requestText: params.requestText,
|
||||||
|
replyText: reply,
|
||||||
|
metadataJson: { requestedBackend },
|
||||||
|
});
|
||||||
|
return appendFastPathReply(reply, buildMasterAgentModelSenderLabel(params.context.effectiveChatPolicy.model));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function tryHandleMasterAgentTakeoverCommand(params: {
|
async function tryHandleMasterAgentTakeoverCommand(params: {
|
||||||
@@ -2099,10 +2126,15 @@ async function tryHandleMasterAgentTakeoverCommand(params: {
|
|||||||
{ globalTakeoverEnabled: nextEnabled },
|
{ globalTakeoverEnabled: nextEnabled },
|
||||||
params.requestedByAccount,
|
params.requestedByAccount,
|
||||||
);
|
);
|
||||||
return appendFastPathReply(
|
const reply = nextEnabled ? "已开启全局接管。" : "已关闭全局接管。";
|
||||||
nextEnabled ? "已开启全局接管。" : "已关闭全局接管。",
|
await tryRecordMasterAgentEvolutionSignal({
|
||||||
buildMasterAgentModelSenderLabel(params.context.effectiveChatPolicy.model),
|
kind: "user_correction",
|
||||||
);
|
account: params.requestedByAccount,
|
||||||
|
requestText: params.requestText,
|
||||||
|
replyText: reply,
|
||||||
|
metadataJson: { globalTakeoverEnabled: nextEnabled },
|
||||||
|
});
|
||||||
|
return appendFastPathReply(reply, buildMasterAgentModelSenderLabel(params.context.effectiveChatPolicy.model));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function tryHandleMasterAgentExecutionModeStatusQuery(params: {
|
async function tryHandleMasterAgentExecutionModeStatusQuery(params: {
|
||||||
@@ -2228,6 +2260,13 @@ async function tryHandleMasterAgentModelCommand(params: {
|
|||||||
|
|
||||||
await updateProjectAgentControls("master-agent", patch, params.requestedByAccount);
|
await updateProjectAgentControls("master-agent", patch, params.requestedByAccount);
|
||||||
const reply = `已把主 Agent 的${scopeLabel}切到 ${requestedModel}。${availableModelsSuffix}`;
|
const reply = `已把主 Agent 的${scopeLabel}切到 ${requestedModel}。${availableModelsSuffix}`;
|
||||||
|
await tryRecordMasterAgentEvolutionSignal({
|
||||||
|
kind: "user_correction",
|
||||||
|
account: params.requestedByAccount,
|
||||||
|
requestText: params.requestText,
|
||||||
|
replyText: reply,
|
||||||
|
metadataJson: { scope, requestedModel },
|
||||||
|
});
|
||||||
return appendFastPathReply(reply, buildMasterAgentModelSenderLabel(requestedModel));
|
return appendFastPathReply(reply, buildMasterAgentModelSenderLabel(requestedModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3219,6 +3258,17 @@ export async function replyToMasterAgentUserMessage(params: {
|
|||||||
return fastIntentResult;
|
return fastIntentResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (/(当前|现在|有没有|是否|哪个|什么|在线吗|状态)/i.test(params.requestText)) {
|
||||||
|
await tryRecordMasterAgentEvolutionSignal({
|
||||||
|
kind: "fast_path_candidate",
|
||||||
|
account: params.requestedByAccount,
|
||||||
|
requestText: params.requestText,
|
||||||
|
metadataJson: {
|
||||||
|
source: "replyToMasterAgentUserMessage.pre_slow_path",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const runtime = await getMasterAgentRuntimeAccount();
|
const runtime = await getMasterAgentRuntimeAccount();
|
||||||
|
|
||||||
if (!runtime?.account) {
|
if (!runtime?.account) {
|
||||||
|
|||||||
226
src/lib/master-agent-evolution.ts
Normal file
226
src/lib/master-agent-evolution.ts
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
import {
|
||||||
|
createMasterAgentEvolutionProposalInState,
|
||||||
|
createMasterAgentEvolutionRuleInState,
|
||||||
|
createUserMasterMemory,
|
||||||
|
getMasterAgentEvolutionState,
|
||||||
|
recordMasterAgentEvolutionSignalInState,
|
||||||
|
updateMasterAgentEvolutionConfig,
|
||||||
|
updateMasterAgentEvolutionProposalStatus,
|
||||||
|
updateProjectAgentControls,
|
||||||
|
} from "@/lib/boss-data";
|
||||||
|
import type {
|
||||||
|
MasterAgentEvolutionProposal,
|
||||||
|
MasterAgentEvolutionProposalType,
|
||||||
|
MasterAgentEvolutionSignalKind,
|
||||||
|
MasterAgentEvolutionMode,
|
||||||
|
} from "@/lib/boss-data";
|
||||||
|
|
||||||
|
const AUTO_APPLY_LOW_RISK_TYPES = new Set<MasterAgentEvolutionProposalType>([
|
||||||
|
"fast_path_rule",
|
||||||
|
"memory_patch",
|
||||||
|
"routing_preference_patch",
|
||||||
|
]);
|
||||||
|
|
||||||
|
function isDeterministicStatusQuestion(text: string) {
|
||||||
|
return /(当前|现在|有没有|是否|什么|哪个|在线|状态|模型|后端|接管|gui|cli|hermes|claw)/i.test(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferProposalFromSignal(input: {
|
||||||
|
kind: MasterAgentEvolutionSignalKind;
|
||||||
|
account: string;
|
||||||
|
projectId: string;
|
||||||
|
requestText: string;
|
||||||
|
signalId: string;
|
||||||
|
}): Omit<
|
||||||
|
Parameters<typeof createMasterAgentEvolutionProposalInState>[0],
|
||||||
|
"status"
|
||||||
|
> | null {
|
||||||
|
const requestText = input.requestText.trim();
|
||||||
|
if (
|
||||||
|
(input.kind === "repeated_question" || input.kind === "fast_path_candidate") &&
|
||||||
|
isDeterministicStatusQuestion(requestText)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
proposalType: "fast_path_rule",
|
||||||
|
account: input.account,
|
||||||
|
projectId: input.projectId,
|
||||||
|
title: `新增 Fast Path:${requestText.slice(0, 32)}`,
|
||||||
|
summary: `检测到确定性问题反复进入主链,建议把“${requestText}”归入本地 Fast Path。`,
|
||||||
|
patchJson: {
|
||||||
|
matcher: requestText,
|
||||||
|
action: "local_status_reply",
|
||||||
|
},
|
||||||
|
sourceSignalIds: [input.signalId],
|
||||||
|
confidence: 0.82,
|
||||||
|
riskLevel: "low",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.kind === "user_correction") {
|
||||||
|
return {
|
||||||
|
proposalType: "memory_patch",
|
||||||
|
account: input.account,
|
||||||
|
projectId: input.projectId,
|
||||||
|
title: "沉淀用户纠正",
|
||||||
|
summary: `检测到用户纠正主Agent行为,建议写入长期工作规则:${requestText}`,
|
||||||
|
patchJson: {
|
||||||
|
scope: "global",
|
||||||
|
memoryType: "workflow_rule",
|
||||||
|
title: "用户纠正 · 工作规则",
|
||||||
|
content: requestText,
|
||||||
|
},
|
||||||
|
sourceSignalIds: [input.signalId],
|
||||||
|
confidence: 0.78,
|
||||||
|
riskLevel: "low",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.kind === "backend_fallback") {
|
||||||
|
return {
|
||||||
|
proposalType: "routing_preference_patch",
|
||||||
|
account: input.account,
|
||||||
|
projectId: input.projectId,
|
||||||
|
title: "调整后端路由偏好",
|
||||||
|
summary: "检测到后端回退,建议后续优先选择最近稳定的可用后端。",
|
||||||
|
patchJson: {
|
||||||
|
backendPreference: "prefer_available_runtime",
|
||||||
|
},
|
||||||
|
sourceSignalIds: [input.signalId],
|
||||||
|
confidence: 0.72,
|
||||||
|
riskLevel: "low",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyLowRiskProposal(proposal: MasterAgentEvolutionProposal) {
|
||||||
|
if (proposal.proposalType === "memory_patch") {
|
||||||
|
const patch = proposal.patchJson as {
|
||||||
|
scope?: "global" | "project";
|
||||||
|
projectId?: string;
|
||||||
|
title?: string;
|
||||||
|
content?: string;
|
||||||
|
memoryType?: "workflow_rule" | "user_preference" | "decision" | "project_progress";
|
||||||
|
tags?: string[];
|
||||||
|
};
|
||||||
|
await createUserMasterMemory({
|
||||||
|
account: proposal.account,
|
||||||
|
scope: patch.scope ?? "global",
|
||||||
|
projectId: patch.scope === "project" ? patch.projectId ?? proposal.projectId : undefined,
|
||||||
|
title: patch.title ?? proposal.title,
|
||||||
|
content: patch.content ?? proposal.summary,
|
||||||
|
memoryType: patch.memoryType ?? "workflow_rule",
|
||||||
|
tags: patch.tags ?? ["evolution"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proposal.proposalType === "routing_preference_patch") {
|
||||||
|
const patch = proposal.patchJson as { backendOverride?: "hermes-runtime" | "claw-runtime" };
|
||||||
|
if (patch.backendOverride) {
|
||||||
|
await updateProjectAgentControls("master-agent", { backendOverride: patch.backendOverride }, proposal.account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await createMasterAgentEvolutionRuleInState({
|
||||||
|
ruleType: proposal.proposalType,
|
||||||
|
account: proposal.account,
|
||||||
|
projectId: proposal.projectId,
|
||||||
|
sourceProposalId: proposal.proposalId,
|
||||||
|
patchJson: proposal.patchJson,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function maybeAutoApplyProposal(proposal: MasterAgentEvolutionProposal) {
|
||||||
|
const state = await getMasterAgentEvolutionState();
|
||||||
|
if (
|
||||||
|
state.config.mode !== "autonomous" ||
|
||||||
|
!state.config.autoApplyLowRiskRules ||
|
||||||
|
proposal.riskLevel !== "low" ||
|
||||||
|
proposal.confidence < 0.75 ||
|
||||||
|
!AUTO_APPLY_LOW_RISK_TYPES.has(proposal.proposalType)
|
||||||
|
) {
|
||||||
|
return proposal;
|
||||||
|
}
|
||||||
|
|
||||||
|
await applyLowRiskProposal(proposal);
|
||||||
|
return updateMasterAgentEvolutionProposalStatus({
|
||||||
|
proposalId: proposal.proposalId,
|
||||||
|
status: "auto_applied",
|
||||||
|
account: proposal.account,
|
||||||
|
note: "autonomous 模式自动采纳低风险提案",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setMasterAgentEvolutionMode(mode: MasterAgentEvolutionMode) {
|
||||||
|
return updateMasterAgentEvolutionConfig({
|
||||||
|
mode,
|
||||||
|
autoApplyLowRiskRules: mode === "autonomous",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listMasterAgentEvolutionSignals() {
|
||||||
|
return (await getMasterAgentEvolutionState()).signals;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listMasterAgentEvolutionProposals() {
|
||||||
|
return (await getMasterAgentEvolutionState()).proposals;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMasterAgentEvolutionDashboard() {
|
||||||
|
return getMasterAgentEvolutionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function recordMasterAgentEvolutionSignal(input: {
|
||||||
|
kind: MasterAgentEvolutionSignalKind;
|
||||||
|
account: string;
|
||||||
|
projectId?: string;
|
||||||
|
requestText: string;
|
||||||
|
replyText?: string;
|
||||||
|
metadataJson?: Record<string, unknown>;
|
||||||
|
}) {
|
||||||
|
const signal = await recordMasterAgentEvolutionSignalInState({
|
||||||
|
kind: input.kind,
|
||||||
|
account: input.account,
|
||||||
|
projectId: input.projectId ?? "master-agent",
|
||||||
|
requestText: input.requestText,
|
||||||
|
replyText: input.replyText,
|
||||||
|
metadataJson: input.metadataJson,
|
||||||
|
});
|
||||||
|
const proposalInput = inferProposalFromSignal({
|
||||||
|
kind: signal.kind,
|
||||||
|
account: signal.account,
|
||||||
|
projectId: signal.projectId,
|
||||||
|
requestText: signal.requestText,
|
||||||
|
signalId: signal.signalId,
|
||||||
|
});
|
||||||
|
if (!proposalInput) {
|
||||||
|
return { signal, proposal: null };
|
||||||
|
}
|
||||||
|
const proposal = await createMasterAgentEvolutionProposalInState(proposalInput);
|
||||||
|
const appliedProposal = await maybeAutoApplyProposal(proposal);
|
||||||
|
return { signal, proposal: appliedProposal };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function approveMasterAgentEvolutionProposal(proposalId: string, account: string) {
|
||||||
|
const proposal = (await getMasterAgentEvolutionState()).proposals.find((item) => item.proposalId === proposalId);
|
||||||
|
if (!proposal) {
|
||||||
|
throw new Error("MASTER_AGENT_EVOLUTION_PROPOSAL_NOT_FOUND");
|
||||||
|
}
|
||||||
|
await applyLowRiskProposal(proposal);
|
||||||
|
return updateMasterAgentEvolutionProposalStatus({
|
||||||
|
proposalId,
|
||||||
|
status: "approved",
|
||||||
|
account,
|
||||||
|
note: "用户批准进化提案",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rejectMasterAgentEvolutionProposal(proposalId: string, account: string) {
|
||||||
|
return updateMasterAgentEvolutionProposalStatus({
|
||||||
|
proposalId,
|
||||||
|
status: "rejected",
|
||||||
|
account,
|
||||||
|
note: "用户拒绝进化提案",
|
||||||
|
});
|
||||||
|
}
|
||||||
73
tests/master-agent-evolution-engine.test.ts
Normal file
73
tests/master-agent-evolution-engine.test.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
||||||
|
|
||||||
|
let runtimeRoot = "";
|
||||||
|
let readState: (typeof import("../src/lib/boss-data"))["readState"];
|
||||||
|
let recordMasterAgentEvolutionSignal: (typeof import("../src/lib/master-agent-evolution"))["recordMasterAgentEvolutionSignal"];
|
||||||
|
let listMasterAgentEvolutionProposals: (typeof import("../src/lib/master-agent-evolution"))["listMasterAgentEvolutionProposals"];
|
||||||
|
let setMasterAgentEvolutionMode: (typeof import("../src/lib/master-agent-evolution"))["setMasterAgentEvolutionMode"];
|
||||||
|
|
||||||
|
async function setup() {
|
||||||
|
if (runtimeRoot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-evolution-engine-"));
|
||||||
|
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
|
||||||
|
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
|
||||||
|
const [data, evolution] = await Promise.all([
|
||||||
|
import("../src/lib/boss-data.ts"),
|
||||||
|
import("../src/lib/master-agent-evolution.ts"),
|
||||||
|
]);
|
||||||
|
readState = data.readState;
|
||||||
|
recordMasterAgentEvolutionSignal = evolution.recordMasterAgentEvolutionSignal;
|
||||||
|
listMasterAgentEvolutionProposals = evolution.listMasterAgentEvolutionProposals;
|
||||||
|
setMasterAgentEvolutionMode = evolution.setMasterAgentEvolutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
test.after(async () => {
|
||||||
|
if (runtimeRoot) {
|
||||||
|
await rm(runtimeRoot, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async () => {
|
||||||
|
await setup();
|
||||||
|
await rm(runtimeRoot, { recursive: true, force: true });
|
||||||
|
await mkdir(runtimeRoot, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("recording repeated deterministic questions creates a pending fast_path_rule proposal in controlled mode", async () => {
|
||||||
|
const result = await recordMasterAgentEvolutionSignal({
|
||||||
|
kind: "repeated_question",
|
||||||
|
projectId: "master-agent",
|
||||||
|
account: "17600003315",
|
||||||
|
requestText: "当前主节点在线吗",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result.signal.kind, "repeated_question");
|
||||||
|
assert.equal(result.proposal?.proposalType, "fast_path_rule");
|
||||||
|
assert.equal(result.proposal?.status, "pending_review");
|
||||||
|
|
||||||
|
const proposals = await listMasterAgentEvolutionProposals();
|
||||||
|
assert.equal(proposals[0]?.proposalType, "fast_path_rule");
|
||||||
|
assert.equal(proposals[0]?.status, "pending_review");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("autonomous mode auto applies low risk fast path proposals as evolution rules", async () => {
|
||||||
|
await setMasterAgentEvolutionMode("autonomous");
|
||||||
|
|
||||||
|
const result = await recordMasterAgentEvolutionSignal({
|
||||||
|
kind: "repeated_question",
|
||||||
|
projectId: "master-agent",
|
||||||
|
account: "17600003315",
|
||||||
|
requestText: "当前主节点在线吗",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result.proposal?.status, "auto_applied");
|
||||||
|
const state = await readState();
|
||||||
|
assert.equal(state.masterAgentEvolutionRules.length, 1);
|
||||||
|
assert.equal(state.masterAgentEvolutionRules[0]?.ruleType, "fast_path_rule");
|
||||||
|
});
|
||||||
125
tests/master-agent-evolution-routes.test.ts
Normal file
125
tests/master-agent-evolution-routes.test.ts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
||||||
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
|
let runtimeRoot = "";
|
||||||
|
let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"];
|
||||||
|
let recordMasterAgentEvolutionSignal: (typeof import("../src/lib/master-agent-evolution"))["recordMasterAgentEvolutionSignal"];
|
||||||
|
let GETEvolutionRoute: (typeof import("../src/app/api/v1/master-agent/evolution/route"))["GET"];
|
||||||
|
let POSTEvolutionConfigRoute: (typeof import("../src/app/api/v1/master-agent/evolution/config/route"))["POST"];
|
||||||
|
let POSTApproveRoute: (typeof import("../src/app/api/v1/master-agent/evolution/proposals/[proposalId]/approve/route"))["POST"];
|
||||||
|
let POSTRejectRoute: (typeof import("../src/app/api/v1/master-agent/evolution/proposals/[proposalId]/reject/route"))["POST"];
|
||||||
|
let AUTH_SESSION_COOKIE = "";
|
||||||
|
|
||||||
|
async function setup() {
|
||||||
|
if (runtimeRoot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-evolution-routes-"));
|
||||||
|
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
|
||||||
|
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
|
||||||
|
const [data, auth, evolution, evolutionRoute, configRoute, approveRoute, rejectRoute] = await Promise.all([
|
||||||
|
import("../src/lib/boss-data.ts"),
|
||||||
|
import("../src/lib/boss-auth.ts"),
|
||||||
|
import("../src/lib/master-agent-evolution.ts"),
|
||||||
|
import("../src/app/api/v1/master-agent/evolution/route.ts"),
|
||||||
|
import("../src/app/api/v1/master-agent/evolution/config/route.ts"),
|
||||||
|
import("../src/app/api/v1/master-agent/evolution/proposals/[proposalId]/approve/route.ts"),
|
||||||
|
import("../src/app/api/v1/master-agent/evolution/proposals/[proposalId]/reject/route.ts"),
|
||||||
|
]);
|
||||||
|
createAuthSession = data.createAuthSession;
|
||||||
|
recordMasterAgentEvolutionSignal = evolution.recordMasterAgentEvolutionSignal;
|
||||||
|
GETEvolutionRoute = evolutionRoute.GET;
|
||||||
|
POSTEvolutionConfigRoute = configRoute.POST;
|
||||||
|
POSTApproveRoute = approveRoute.POST;
|
||||||
|
POSTRejectRoute = rejectRoute.POST;
|
||||||
|
AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createAdminRequest(url: string, body?: unknown) {
|
||||||
|
const session = await createAuthSession({
|
||||||
|
account: "17600003315",
|
||||||
|
role: "highest_admin",
|
||||||
|
displayName: "Boss 超级管理员",
|
||||||
|
loginMethod: "password",
|
||||||
|
});
|
||||||
|
return new NextRequest(url, {
|
||||||
|
method: body === undefined ? "GET" : "POST",
|
||||||
|
headers: {
|
||||||
|
cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`,
|
||||||
|
...(body === undefined ? {} : { "content-type": "application/json" }),
|
||||||
|
},
|
||||||
|
body: body === undefined ? undefined : JSON.stringify(body),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test.after(async () => {
|
||||||
|
if (runtimeRoot) {
|
||||||
|
await rm(runtimeRoot, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async () => {
|
||||||
|
await setup();
|
||||||
|
await rm(runtimeRoot, { recursive: true, force: true });
|
||||||
|
await mkdir(runtimeRoot, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("GET /api/v1/master-agent/evolution returns config proposals and rules", async () => {
|
||||||
|
const response = await GETEvolutionRoute(
|
||||||
|
await createAdminRequest("http://127.0.0.1:3000/api/v1/master-agent/evolution"),
|
||||||
|
);
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
const payload = await response.json() as { ok: boolean; proposals: unknown[]; rules: unknown[] };
|
||||||
|
assert.equal(payload.ok, true);
|
||||||
|
assert.ok(Array.isArray(payload.proposals));
|
||||||
|
assert.ok(Array.isArray(payload.rules));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("POST /api/v1/master-agent/evolution/config switches mode", async () => {
|
||||||
|
const response = await POSTEvolutionConfigRoute(
|
||||||
|
await createAdminRequest("http://127.0.0.1:3000/api/v1/master-agent/evolution/config", { mode: "autonomous" }),
|
||||||
|
);
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
const payload = await response.json() as { ok: boolean; config?: { mode?: string } };
|
||||||
|
assert.equal(payload.ok, true);
|
||||||
|
assert.equal(payload.config?.mode, "autonomous");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("approve and reject evolution proposals update proposal status", async () => {
|
||||||
|
const created = await recordMasterAgentEvolutionSignal({
|
||||||
|
kind: "repeated_question",
|
||||||
|
account: "17600003315",
|
||||||
|
requestText: "当前主节点在线吗",
|
||||||
|
});
|
||||||
|
const proposalId = created.proposal?.proposalId ?? "";
|
||||||
|
assert.ok(proposalId);
|
||||||
|
|
||||||
|
const approveResponse = await POSTApproveRoute(
|
||||||
|
await createAdminRequest(`http://127.0.0.1:3000/api/v1/master-agent/evolution/proposals/${proposalId}/approve`, {}),
|
||||||
|
{ params: Promise.resolve({ proposalId }) },
|
||||||
|
);
|
||||||
|
assert.equal(approveResponse.status, 200);
|
||||||
|
const approvePayload = await approveResponse.json() as { ok: boolean; proposal?: { status?: string } };
|
||||||
|
assert.equal(approvePayload.ok, true);
|
||||||
|
assert.equal(approvePayload.proposal?.status, "approved");
|
||||||
|
|
||||||
|
const created2 = await recordMasterAgentEvolutionSignal({
|
||||||
|
kind: "backend_fallback",
|
||||||
|
account: "17600003315",
|
||||||
|
requestText: "切到稳定后端",
|
||||||
|
});
|
||||||
|
const proposalId2 = created2.proposal?.proposalId ?? "";
|
||||||
|
assert.ok(proposalId2);
|
||||||
|
const rejectResponse = await POSTRejectRoute(
|
||||||
|
await createAdminRequest(`http://127.0.0.1:3000/api/v1/master-agent/evolution/proposals/${proposalId2}/reject`, {}),
|
||||||
|
{ params: Promise.resolve({ proposalId: proposalId2 }) },
|
||||||
|
);
|
||||||
|
assert.equal(rejectResponse.status, 200);
|
||||||
|
const rejectPayload = await rejectResponse.json() as { ok: boolean; proposal?: { status?: string } };
|
||||||
|
assert.equal(rejectPayload.ok, true);
|
||||||
|
assert.equal(rejectPayload.proposal?.status, "rejected");
|
||||||
|
});
|
||||||
49
tests/master-agent-evolution-state.test.ts
Normal file
49
tests/master-agent-evolution-state.test.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
||||||
|
|
||||||
|
let runtimeRoot = "";
|
||||||
|
let readState: (typeof import("../src/lib/boss-data"))["readState"];
|
||||||
|
let updateMasterAgentEvolutionConfig: (typeof import("../src/lib/boss-data"))["updateMasterAgentEvolutionConfig"];
|
||||||
|
|
||||||
|
async function setup() {
|
||||||
|
if (runtimeRoot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-evolution-state-"));
|
||||||
|
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;
|
||||||
|
updateMasterAgentEvolutionConfig = data.updateMasterAgentEvolutionConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
test.after(async () => {
|
||||||
|
if (runtimeRoot) {
|
||||||
|
await rm(runtimeRoot, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async () => {
|
||||||
|
await setup();
|
||||||
|
await rm(runtimeRoot, { recursive: true, force: true });
|
||||||
|
await mkdir(runtimeRoot, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("boss state 初始化时包含 master agent evolution 默认配置与空集合", async () => {
|
||||||
|
const state = await readState();
|
||||||
|
assert.equal(state.masterAgentEvolutionConfig.mode, "controlled");
|
||||||
|
assert.equal(state.masterAgentEvolutionConfig.autoApplyLowRiskRules, false);
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionSignals, []);
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionProposals, []);
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionRules, []);
|
||||||
|
assert.deepEqual(state.masterAgentEvolutionRunLogs, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("master agent evolution 配置可切换 autonomous 并自动打开低风险自动采纳", async () => {
|
||||||
|
const config = await updateMasterAgentEvolutionConfig({ mode: "autonomous" });
|
||||||
|
assert.equal(config.mode, "autonomous");
|
||||||
|
assert.equal(config.autoApplyLowRiskRules, true);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user