# 13 · 多 Agent 功能复盘与落地方案

> 复盘日期: 2026-04-16

## 1. 三项目多 Agent 实现方式对比

### 1.1 Toonflow — 三层 Agent 架构（最成熟）

**架构模式**: Decision-Execution-Supervision (DES)

```
用户 ←→ 决策层 Agent (唯一入口)
              │
              ├── 执行层 SubAgent × N (每个是一个 @Tool)
              │     ├── run_sub_agent_storySkeleton      (故事骨架)
              │     ├── run_sub_agent_adaptationStrategy  (改编策略)
              │     ├── run_sub_agent_script              (剧本编写)
              │     ├── run_sub_agent_derive_assets       (衍生资产分析)
              │     ├── run_sub_agent_generate_assets     (衍生资产生成)
              │     ├── run_sub_agent_director_plan       (导演规划)
              │     ├── run_sub_agent_storyboard_table    (分镜表)
              │     ├── run_sub_agent_storyboard_panel    (分镜面板写入)
              │     └── run_sub_agent_storyboard_gen      (分镜图生成)
              │
              └── 监督层 SubAgent × 1
                    └── run_supervision_agent / run_sub_agent_supervision
```

**核心实现要点**:

| 要素 | 实现方式 | 文件位置 |
|------|---------|---------|
| 决策入口 | `decisionAI()` 函数, Vercel AI SDK `stream()` | `src/agents/{script,production}Agent/index.ts` |
| SubAgent 创建 | `createSubAgent()` — 每个 executor 是一个 `tool()` | 同上, `tool()` from `ai` |
| Agent 通信 | 父 Agent 调用 `tool → runAgent()` → 子 Agent 独立 LLM 调用 | `runAgent()` 内部函数 |
| Skill 加载 | `fs.readFile(skill.md)` → system prompt | `src/utils/agent/skillsTools.ts` |
| 向量记忆 | 本地 ONNX `all-MiniLM-L6-v2` + cosine similarity | `src/utils/agent/memory.ts` |
| 记忆三层 | shortTerm(5条) + summaries(10条) + RAG(3条) | `Memory.get()` |
| 自动摘要 | 每 3 条 message 触发 AI 压缩 | `Memory.add()` |
| 深度检索 | 向量搜索 summary → AI 判断相关性 → 展开原始 messages | `deepRetrieve()` |
| 实时推送 | Socket.IO `ResTool` 流式消息 | `src/socket/resTool.ts` |

**关键设计亮点**:

1. **决策层不执行、不读数据** — 严格的职责分离, 避免 Agent 越权
2. **子 Agent 作为 Tool** — 用 Vercel AI SDK 的 `tool()` 包装子 Agent, 决策层 LLM 自主决定何时调用
3. **监督层独立审核** — 生成 A/B/C/D 评分报告, 用户确认后才进入下一阶段
4. **Skill 热加载** — `activate_skill` 工具按需加载提示词, 不一次性全塞进 context
5. **159 个 Markdown Skill 文件** — 8 种艺术风格 x (角色/场景/道具/视频/导演) + 剧本/制作流水线

**两个独立 Agent 系统**:

| Agent | 入口 | 执行层子 Agent | 流水线 |
|-------|------|---------------|--------|
| ScriptAgent | `scriptAgent/index.ts` | storySkeleton, adaptationStrategy, script | 初始化 → 故事骨架 → 改编策略 → 剧本编写 |
| ProductionAgent | `productionAgent/index.ts` | derive_assets, generate_assets, director_plan, storyboard_table, storyboard_panel, storyboard_gen | 衍生资产 → 导演规划 → 分镜表 → 面板写入 → 图生成 |

**决策层 Skill 指令核心规范** (来自 `script_agent_decision.md` / `production_agent_decision.md`):

- 决策层不读取工作区数据, 所有工作区读取由执行层自行完成
- SubAgent 失败时决策层不得接管, 必须向用户汇报失败并终止
- 派发给执行层的指令正文严格不超过 100 字
- 阶段必须串行, 审核与执行串行(先执行后审核)
- 审核结果展示后必须等待用户回复, 不可自行派发新任务

**监督层评分标准** (来自 `script_agent_supervision.md`):

| 评分 | 严重问题 | 中等问题 | 处置 |
|------|----------|----------|------|
| A — 可直接使用 | 0 | ≤2 | 直接进入下一阶段 |
| B — 小修后可用 | 0 | ≤5 | 小问题, 是否修复或继续? |
| C — 需较大修改 | 1-2 | 不限 | 建议修复, 用户选择修复项 |
| D — 建议重做 | ≥3 | 不限 | 建议重做此阶段 |

**记忆系统实现细节** (来自 `src/utils/agent/memory.ts`):

```
Memory 类
├── add(role, content)           → 存消息 + embedding
│   └── 每 3 条未总结消息 → generateSummary() → 存 summary
├── get(text)                    → 返回 {shortTerm, summaries, rag}
│   ├── shortTerm: 最近 5 条未总结消息
│   ├── summaries: 最近 10 条摘要
│   └── rag: 向量搜索所有消息, 返回 top-3
├── deepRetrieve(keyword)        → 向量搜索 summary → AI 判相关性 → 展开原始消息
└── getTools()                   → 暴露为 LLM 可调用的 tool
```

隔离键: `${projectId}:${agentType}${episodesId?:...}` — 按项目/Agent 类型/集数隔离

---

### 1.2 AIComicBuilder — 任务队列 + 线性管道（非 Agent 架构）

**架构模式**: 任务队列驱动的确定性管道

```
用户点击 → Server Action (generateXxx)
              │
              ├── buildPrompt(模板 + 插槽)
              ├── callAI(OpenAI/Gemini)
              ├── parseResponse(JSON/XML)
              ├── saveToDB(Drizzle)
              └── 返回结果
```

AIComicBuilder **没有 Agent 架构**, 而是用 Next.js Server Actions 实现线性工作流。

**核心实现要点**:

| 要素 | 实现方式 | 文件位置 |
|------|---------|---------|
| 任务队列 | SQLite 持久化 + 2 秒轮询 dequeue | `src/lib/task-queue/{queue.ts, worker.ts}` |
| 8 个管道处理器 | script_outline → parse → character_extract → shot_split → frame → video → assemble | `src/lib/pipeline/*.ts` |
| 自动链式触发 | parse 完成后自动入队 character_extract | `src/lib/pipeline/script-parse.ts:39-50` |
| 批量并发 | `Promise.allSettled()` 批量生成帧/视频 | `src/app/api/projects/[id]/generate/route.ts` |
| 版本控制 | `storyboardVersions` 表 + `shotAssets.assetVersion + isActive` | `src/lib/shot-asset-utils.ts` |
| 提示词注册 | 动态插槽注入, per user/project 覆盖, 三级优先级 | `src/lib/ai/prompts/registry.ts` |
| Provider 路由 | 工厂模式分发到 OpenAI/Gemini/Kling/Veo/SeedDance | `src/lib/ai/provider-factory.ts` |
| 原子 dequeue | SQL 子查询防止多实例竞争 | `src/lib/task-queue/worker.ts` |

**12 个提示词分类**:

| 类别 | 提示词 |
|------|--------|
| 剧本 | script_generate, script_parse, script_split |
| 角色 | character_extract, import_character_extract, character_image |
| 分镜 | shot_split |
| 帧生成 | frame_generate_first, frame_generate_last, scene_frame_generate |
| 视频 | video_generate, ref_video_prompt_generate |

**三级覆盖**: 项目级覆盖 > 全局级覆盖 > 代码默认值, 且支持版本历史 + 一键回滚。

**与 Agent 架构的本质差异**: 没有 LLM 自主决策能力, 每一步都是用户手动触发或前一步自动触发, AI 只在单步内起作用。但其**版本控制和批量操作**是三个项目中最成熟的。

---

### 1.3 waoowaoo — 任务队列 + 步骤编排器（准 Agent）

**架构模式**: BullMQ Queue Workers + Step-based Orchestrator

```
API → TaskSubmitter
        │
        ├── 预检(计费、去重、幂等)
        ├── 入队(4 个专用队列)
        └── Worker 消费
              │
              ├── withTaskLifecycle() 包装
              ├── withUserConcurrencyGate() 限流
              ├── 调用 AI Provider
              ├── 进度上报 (reportTaskProgress)
              └── 计费结算

Orchestrator (story-to-script / script-to-storyboard)
        │
        ├── Step 1: character analysis
        ├── Step 2: location analysis
        ├── Step 3: prop analysis
        └── Step 4: screenplay generation
        (依赖图 + 并行键 + 重试恢复)
```

**核心实现要点**:

| 要素 | 实现方式 | 文件位置 |
|------|---------|---------|
| 4 个 BullMQ 队列 | image / video / voice / text | `src/lib/task/queues.ts` |
| 任务提交 | 归一化 payload + 计费预检 + 去重 + 入队 | `src/lib/task/submitter.ts` |
| Worker 模式 | 按 TaskType 路由到 handler, composition 模式 | `src/lib/workers/{image,video,voice,text}.worker.ts` |
| 任务生命周期 | `withTaskLifecycle()` 统一封装 | `src/lib/workers/shared.ts` |
| 并发控制 | `withUserConcurrencyGate()` 用户级限流 | 同上 |
| 计费 | freeze → settle/rollback 三步原子 | `src/lib/billing/service.ts` |
| 编排器 | step-based, 依赖图解析, retryStepKey 恢复 | `src/lib/run-runtime/*.ts` |
| 进度推送 | SSE stream, Run 事件桥接 | `src/lib/workers/shared.ts` |
| 重试策略 | 指数退避 `2s * 2^(n-1)`, 非可重试错误直接终止 | Worker 配置 |

**默认队列配置**: 5 次重试, 指数退避(2s 基础延迟), 自动清理 500 条完成/失败任务。

**编排器特性**:
- 多步骤工作流: character → location → prop → screenplay
- 并行执行: `mapWithConcurrency` 批量匹配
- 依赖解析: `dependsOn`, `blockedBy`, `parallelKey`, `groupId`
- 失败恢复: `retryStepKey` 从失败步骤重启
- LLM 流式回调: chunk 级别可观测性
- Run 租约协调: 防止并发修改
- 推理努力控制: minimal/low/medium/high

**计费模式**:

| 模式 | 行为 |
|------|------|
| OFF | 完全跳过计费(开发/开源) |
| SHADOW | 只记录不拦截(观察期) |
| ENFORCE | 余额不足拒绝任务(生产) |

**与 Toonflow Agent 的核心差异**: waoowaoo 的 "Agent" 更像是**状态机驱动的工作流**, 没有 LLM 自主决策能力。编排器按预定义的 step 序列执行, 不会根据中间结果动态调整路径。但其**任务队列、计费、并发控制**是三个项目中最企业级的。

---

## 2. 三项目本质区别总结

```
Toonflow:       LLM 自主决策 → 调用 Tool(子Agent) → 监督审核 → 人类确认
                ↑ 真正的 Agent, LLM 控制执行流程

AIComicBuilder: 用户点击 → 固定管道(A→B→C→D) → 批量并发 → 版本管理
                ↑ 确定性管道, 无 LLM 决策, 但版本控制和批量操作最成熟

waoowaoo:       API → BullMQ 队列 → Worker 消费 → Orchestrator step 编排
                ↑ 准 Agent, 有依赖图和步骤编排, 但步骤序列预定义
```

| 维度 | Toonflow | AIComicBuilder | waoowaoo |
|------|----------|----------------|----------|
| LLM 自主决策 | **有**(决策层选择调用哪个子 Agent) | 无 | 无 |
| 执行流程动态性 | **高**(LLM 根据中间结果调整路径) | 低(固定管道) | 中(预定义 step 但有依赖图) |
| 监督审核 | **有**(独立监督层 + A/B/C/D 评分) | 无 | 无 |
| 人类在环 | **有**(审核后等待用户确认) | 有(手动触发每步) | 部分(提交后全自动) |
| 任务队列 | 无(同步调用) | SQLite 轮询 | **BullMQ + Redis** |
| 计费集成 | 无 | 无 | **有**(余额冻结) |
| 并发控制 | 无 | `Promise.allSettled` | **用户级 Gate** |
| 版本控制 | 无 | **有**(assetVersion + isActive) | 有(MediaObject 哈希去重) |
| 断点续跑 | 无 | 有(任务状态持久化) | 有(retryStepKey) |
| 记忆系统 | **有**(向量 + 摘要 + RAG) | 无 | 无 |
| Skill 外化 | **有**(159 个 .md 文件) | 12 个提示词 | 无 |

---

## 3. 我们项目的多 Agent 落地方案 (Java + Spring AI Alibaba)

### 3.1 三层 Agent 的 Java 落地（对标 Toonflow）

Toonflow 用 Vercel AI SDK 的 `tool()` 把子 Agent 变成决策层可调用的工具。Spring AI Alibaba 用 `@Tool` 注解实现完全等价的模式:

```
                    ┌─────────────────────────┐
                    │  ScriptDecisionAgent    │
                    │  @Tool run_skeleton     │
                    │  @Tool run_adaptation   │
                    │  @Tool run_script       │
                    │  @Tool run_supervision  │
                    └────────┬────────────────┘
                             │ ChatClient + tools(this)
                             │ LLM 自主选择调用哪个 @Tool
                             ▼
              ┌──────────────────────────────┐
              │ SkeletonExecutor             │
              │   loadSkill("skeleton.md")   │
              │   chatClient.prompt(...)     │
              │   supervisionAgent.review()  │ ← 可选
              │   return result              │
              └──────────────────────────────┘
```

**关键差异与改进**:

| Toonflow 方式 | 我们的 Java 落地 | 原因 |
|---|---|---|
| 子 Agent 在同一进程同步调用 | 重型任务走 MQ 异步(图/视频), 轻型任务(文本 Agent)同步 | 避免 HTTP 超时, 支持水平扩展 |
| Socket.IO 推送 | SSE(Spring WebFlux `Flux<ServerSentEvent>`) | 设计文档已确定, 更简单 |
| 本地 ONNX 向量化 | Redis Stack RediSearch + 通义 embedding | 分布式, Spring AI `RedisVectorStore` |
| 每次全量搜索 memory | `VectorStore.similaritySearch` + FilterExpression | Spring AI 原生支持 |
| 无 GraphRun/Step 追踪 | **必须加上** — `graph_runs` + `graph_steps` 表 | Toonflow 的最大缺失 |

### 3.2 我们需要补上的关键能力（三项目均缺失或部分缺失）

| 能力 | 说明 | 参考来源 | 对应模块 |
|---|---|---|---|
| **GraphRun/GraphStep 持久化** | 每次 Agent 流程创建 Run, 每步创建 Step, 支持断点续跑 + 可视化回放 | 设计文档 08-6.1 | `yaoai-agent` |
| **任务队列化** | 图/视频/TTS 必须走 RabbitMQ, 不能在 Agent 内同步调用 | waoowaoo 模式 | `yaoai-task` |
| **计费集成** | 每个 AI 调用前冻结余额, 完成后结算 | waoowaoo 模式 | `yaoai-billing` |
| **多租户隔离** | Agent 记忆按 `tenantId + projectId` 隔离 | 自研设计 | `yaoai-agent` |
| **Skill 分级覆盖** | 项目级 > 租户级 > 全局级 > 文件默认 | AIComicBuilder | `yaoai-ai-core` |
| **并发控制** | 同一用户最多 N 个并发 Agent 流程 | waoowaoo Gate | `yaoai-task` |
| **任务自动链式触发** | Worker 完成后自动发布下一步消息, 确定性管道不回 Agent | AIComicBuilder | `yaoai-pipeline` |
| **批量并发提交** | 首尾帧和视频生成批量并发提交到 MQ | AIComicBuilder `allSettled` | `yaoai-pipeline` |

### 3.3 Executor 执行方式分类

| 类型 | Executor | 执行方式 | 原因 |
|------|----------|---------|------|
| **同步文本** | SkeletonExecutor | Agent 内 ChatClient 调用 | 纯文本, 秒级响应 |
| **同步文本** | AdaptationExecutor | 同上 | |
| **同步文本** | ScriptExecutor | 同上 | |
| **同步文本** | DeriveAssetExecutor | 同上 | |
| **同步文本** | DirectorPlanExecutor | 同上 | |
| **同步文本** | StoryboardTableExecutor | 同上 | |
| **同步文本** | StoryboardPanelExecutor | 同上 | |
| **异步队列** | AssetImageExecutor | 提交 MQ `image.generate` | 分钟级, 需重试 |
| **异步队列** | FrameGenerateExecutor | 提交 MQ `image.generate` | 同上 |
| **异步队列** | VideoGenerateExecutor | 提交 MQ `video.generate` | 10+ 分钟, 需轮询 |
| **异步队列** | VoiceTtsExecutor | 提交 MQ `voice.tts` | 秒级但量大 |
| **异步队列** | AssembleExecutor | 提交 MQ `video.assemble` | FFmpeg 耗时 |

### 3.4 核心流程伪代码

```java
@Service
public class ProductionAgentOrchestrator {

    public GraphRun start(Long projectId, String userGoal) {
        // 1. 创建 GraphRun
        GraphRun run = graphRunRepo.create(projectId, "PRODUCTION", userGoal);

        // 2. 决策层: LLM 分析意图, 返回执行计划
        GraphStep decisionStep = step(run, "decision", () -> {
            String skill = skillLoader.load("production_agent_decision");
            MemoryContext mem = memoryService.retrieve(projectId, userGoal);
            return chatClient.prompt()
                .system(skill + mem.asPromptSuffix())
                .user(userGoal)
                .tools(executorTools)  // @Tool 包装的各 Executor
                .call();
        });

        // 3. 决策层 LLM 会自主调用 @Tool, 每次调用创建一个 GraphStep
        //    同步 Executor 直接返回结果
        //    异步 Executor 提交 MQ 任务, 返回 taskId

        // 4. 监督层审核(决策层 LLM 自动触发 run_supervision)
        //    返回 A/B/C/D 评分报告

        // 5. SSE 推送进度给前端
        sseService.publish(projectId, "agent.step", decisionStep);

        return run;
    }
}
```

### 3.5 Skill 加载与三级覆盖

```java
@Service
public class SkillLoader {
    // 文件系统: classpath:skills/ + 外部 config 目录
    // 数据库: prompt_templates 表

    public String loadRendered(String key, Long tenantId, Long projectId, Map<String,String> vars) {
        // 1. 查 prompt_templates 表 scope=PROJECT
        // 2. 查 scope=TENANT
        // 3. 查 scope=GLOBAL
        // 4. fallback 到文件系统(Toonflow 的 .md 文件)
        // 5. Mustache 模板渲染(插入角色、场景、风格等变量)
    }
}
```

优先级链: **项目级 DB 覆盖 > 租户级 DB > 全局 DB > 文件默认值**

热更新: 文件变更监听 + Redis pub/sub 通知所有节点失效缓存。

### 3.6 Agent 记忆系统(Redis Stack 版)

```java
@Service
public class AgentMemoryService {
    @Autowired VectorStore vectorStore;  // RedisVectorStore

    // 对标 Toonflow Memory.add()
    public void add(String projectId, String role, String content) {
        vectorStore.add(List.of(new Document(content, Map.of(
            "project_id", projectId,
            "agent_type", "script",
            "role", role,
            "created_at", System.currentTimeMillis()
        ))));
        // 定期摘要压缩: 超过 100 条触发
    }

    // 对标 Toonflow Memory.get()
    public MemoryContext retrieve(String projectId, String query) {
        var results = vectorStore.similaritySearch(
            SearchRequest.query(query).withTopK(3)
                .withFilterExpression("project_id == '" + projectId + "'")
        );
        return new MemoryContext(results, shortTerm, summaries);
    }

    // 对标 Toonflow Memory.deepRetrieve()
    public List<Document> deepRetrieve(String projectId, String keyword) {
        // 1. 向量搜索 summary 类型文档
        // 2. AI 判断相关性
        // 3. 展开 relatedMessageIds 查原始消息
    }
}
```

---

## 4. 吸收策略总结

### 4.1 按来源分类

| 吸收维度 | 来源项目 | 我们的对应模块 |
|----------|---------|--------------|
| 三层 Agent 架构(DES) | **Toonflow** | `yaoai-agent` |
| 决策/执行/监督 Skill 指令体系 | **Toonflow** | `yaoai-agent` + `skills/` |
| Agent 向量记忆 + 自动摘要 | **Toonflow** | `yaoai-agent` (Redis Stack) |
| 8 种艺术风格手册 | **Toonflow** | `skills/art_skills/` |
| 提示词版本管理 + 三级覆盖 | **AIComicBuilder** | `yaoai-ai-core` |
| 资产版本控制(assetVersion + isActive) | **AIComicBuilder** | `yaoai-domain` |
| 任务自动链式触发 | **AIComicBuilder** | `yaoai-pipeline` |
| 批量并发(allSettled) | **AIComicBuilder** | `yaoai-pipeline` |
| BullMQ → RabbitMQ 任务队列 | **waoowaoo** | `yaoai-task` |
| 余额冻结计费 | **waoowaoo** | `yaoai-billing` |
| 用户级并发控制 Gate | **waoowaoo** | `yaoai-task` |
| 幂等性(idempotencyKey) | **waoowaoo** | `yaoai-task` |
| GraphRun/GraphStep 持久化 | **自研**(三项目均缺失) | `yaoai-agent` |

### 4.2 按优先级排列

| 优先级 | 功能 | 来源 | 里程碑 |
|--------|------|------|--------|
| P0 | ScriptAgent 三层架构(决策+执行+监督) | Toonflow | M2 |
| P0 | ProductionAgent 三层架构 | Toonflow | M3 |
| P0 | Skill 文件外化 + 热加载 | Toonflow | M1 |
| P0 | 异步任务队列 (RabbitMQ) | waoowaoo | M1 |
| P0 | 余额冻结计费 | waoowaoo | M1 |
| P1 | GraphRun/GraphStep 持久化 | 自研 | M2 |
| P1 | Agent 向量记忆 (Redis Stack) | Toonflow→Redis | M2 |
| P1 | Skill 分级覆盖 + 版本历史 | AIComicBuilder | M3 |
| P1 | 监督层 A/B/C/D 评分体系 | Toonflow | M2 |
| P1 | 资产版本控制 | AIComicBuilder | M2 |
| P2 | 记忆自动摘要压缩 | Toonflow | M4 |
| P2 | 深度记忆检索 (deepRetrieve) | Toonflow | M4 |
| P2 | 任务自动链式触发 | AIComicBuilder | M3 |
| P2 | 断点续跑(GraphStep 恢复) | 自研 | M5 |

---

## 5. 关键结论

1. **Toonflow 的"子 Agent 作为 Tool"模式已被验证可行** — Spring AI Alibaba 的 `@Tool` + `ChatClient` 可以完全复刻, 且 Java 的类型安全和 Spring 生态使代码更健壮

2. **三个项目都缺少 GraphRun/GraphStep 持久化** — 这是我们的差异化优势, 支持断点续跑和可视化回放

3. **确定性管道不需要经过 Agent 决策** — 参考 AIComicBuilder 的链式触发, 像 "parse → extract → split" 这种确定性流程直接在 Worker 间链式触发, 比回到 Agent 决策层更高效

4. **计费必须与任务系统深度耦合** — 参考 waoowaoo 的 freeze/settle/rollback 模式, 每个 MQ 任务都必须带 freezeId

5. **Skill 热加载是关键竞争力** — 159 个 Markdown 文件的 Skill 库 + 数据库覆盖 + 热更新, 让非技术用户也能调优 AI 行为

---

*生成时间: 2026-04-16*
