/** * Task Orchestrator — 任务编排器 * * 支持父子委派模式:主 Agent 委派子任务给 SubAgent。 * 每个 SubAgent 运行在**独立的 AgentLoopEngine 实例**中,避免状态污染。 * * 安全保障: * 1. 独立引擎实例 — SubAgent 不共享主 Agent 的引擎状态 * 2. 递归深度限制 — 默认最大 3 层,防止无限递归 * 3. 工具白名单隔离 — SubAgent 默认不继承 delegate_task(防止递归) * 4. 真正的 abort — 通过引擎引用调用 engine.abort() * 5. 事件隔离 — SubAgent 的流式事件不直接转发到前端,仅通过 orchestrator 事件通知 * * @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第五章 */ import { EventEmitter } from 'events'; import { nanoid } from 'nanoid'; import { AgentLoopEngine } from '../agent-loop/engine'; import type { AgentLoopConfig } from '../agent-loop/types'; import type { MetonaMessage, MetonaSystemPrompt, MetonaToolDef } from '../types'; import type { ToolRegistry } from '../tools/registry'; import type { PreToolHook } from '../hooks/pre-tool'; import type { PostToolHook } from '../hooks/post-tool'; import log from 'electron-log'; export interface SubAgentResult { taskId: string; result: string; success: boolean; durationMs: number; iterations: number; } interface SubAgentHandle { taskId: string; description: string; status: 'pending' | 'running' | 'completed' | 'error'; depth: number; engine?: AgentLoopEngine; result?: SubAgentResult; abort: () => void; getStatus: () => { taskId: string; status: string; description: string; depth: number }; } /** 默认递归深度限制 */ const MAX_DELEGATION_DEPTH = 3; export class TaskOrchestrator extends EventEmitter { private activeSubAgents = new Map(); /** 追踪每个 session 的当前委派深度 */ private sessionDepth = new Map(); constructor( private mainEngine: AgentLoopEngine, private toolRegistry?: ToolRegistry, private preToolHooks: PreToolHook[] = [], private postToolHooks: PostToolHook[] = [], private defaultConfig?: Partial, ) { super(); } /** * 委派子任务 * * 创建一个独立的 AgentLoopEngine 实例执行子任务。 * SubAgent 不共享主 Agent 的引擎状态,安全隔离。 */ async delegate(params: { taskId?: string; description: string; parentSessionId: string; maxIterations?: number; tools?: string[]; }): Promise { const taskId = params.taskId ?? `sub_${nanoid(8)}`; const startMs = Date.now(); // ===== 递归深度检查 ===== const currentDepth = this.sessionDepth.get(params.parentSessionId) ?? 0; if (currentDepth >= MAX_DELEGATION_DEPTH) { log.warn(`[Orchestrator] Delegation depth limit reached (${currentDepth}) for session ${params.parentSessionId}`); return { taskId, result: `SubAgent delegation depth limit reached (${MAX_DELEGATION_DEPTH}). Cannot delegate further.`, success: false, durationMs: 0, iterations: 0, }; } const depth = currentDepth + 1; this.sessionDepth.set(params.parentSessionId, depth); this.emit('taskDelegated', { taskId, description: params.description, parentSessionId: params.parentSessionId, depth }); // ===== 创建独立的引擎实例 ===== const subEngine = new AgentLoopEngine( { maxIterations: params.maxIterations ?? 10, totalTimeoutMs: 300_000, // 子任务总超时 5 分钟 thinkingEnabled: this.defaultConfig?.thinkingEnabled ?? true, thinkingEffort: this.defaultConfig?.thinkingEffort ?? 'medium', contextLength: this.defaultConfig?.contextLength, contextWindow: this.defaultConfig?.contextWindow ?? 128_000, }, this.mainEngine.getAdapter(), this.toolRegistry, this.preToolHooks, this.postToolHooks, ); subEngine.setWorkspacePath(this.mainEngine.getWorkspacePath()); // ===== 工具白名单设置 ===== const allowedTools = this.resolveTools(params.tools); subEngine.setTools(allowedTools); const handle: SubAgentHandle = { taskId, description: params.description, status: 'running', depth, engine: subEngine, abort: () => { subEngine.abort(); handle.status = 'error'; this.activeSubAgents.delete(taskId); }, getStatus: () => ({ taskId, status: handle.status, description: handle.description, depth: handle.depth }), }; this.activeSubAgents.set(taskId, handle); this.emit('taskStarted', { taskId, depth }); try { // 构建用户消息 const userMessage: MetonaMessage = { role: 'user', content: params.description, timestamp: Date.now(), }; // 构建 System Prompt(子 Agent 专用) const systemPrompt = this.buildSubAgentPrompt(params.description, depth); // 运行 Agent Loop(同步等待完成) const output = await subEngine.runStream( userMessage, taskId, [], // 子 Agent 无历史 systemPrompt, ); const durationMs = Date.now() - startMs; const success = output.terminationReason === 'completed'; const result: SubAgentResult = { taskId, result: output.finalAnswer, success, durationMs, iterations: output.iterations.length, }; handle.status = success ? 'completed' : 'error'; handle.result = result; this.activeSubAgents.delete(taskId); // v0.3.0 修复: 深度恢复为 0 时删除条目,避免 sessionDepth Map 随会话累积 if (currentDepth === 0) { this.sessionDepth.delete(params.parentSessionId); } else { this.sessionDepth.set(params.parentSessionId, currentDepth); } this.emit('taskCompleted', result); log.info(`[Orchestrator] SubAgent "${taskId}" (depth=${depth}) ${success ? 'completed' : 'failed'} in ${durationMs}ms, ${output.iterations.length} iterations`); return result; } catch (error) { const durationMs = Date.now() - startMs; const errMsg = (error as Error).message; const result: SubAgentResult = { taskId, result: errMsg, success: false, durationMs, iterations: 0, }; handle.status = 'error'; handle.result = result; this.activeSubAgents.delete(taskId); // v0.3.0 修复: 深度恢复为 0 时删除条目,避免 sessionDepth Map 随会话累积 if (currentDepth === 0) { this.sessionDepth.delete(params.parentSessionId); } else { this.sessionDepth.set(params.parentSessionId, currentDepth); } this.emit('taskError', { taskId, error: errMsg }); log.error(`[Orchestrator] SubAgent "${taskId}" (depth=${depth}) error: ${errMsg}`); return result; } } /** * 解析工具白名单 * * - 如果指定了 tools,使用白名单(自动排除 delegate_task 防止递归) * - 如果未指定,使用所有已启用工具(同样排除 delegate_task) */ private resolveTools(toolNames?: string[]): MetonaToolDef[] { if (!this.toolRegistry) return []; // 始终排除 delegate_task 防止递归(除非深度为 1 且显式要求) const EXCLUDE_TOOLS = new Set(['delegate_task']); if (toolNames && toolNames.length > 0) { // 使用白名单模式 const resolved: MetonaToolDef[] = []; const notFound: string[] = []; for (const name of toolNames) { if (EXCLUDE_TOOLS.has(name)) continue; // 静默排除 const tool = this.toolRegistry.get(name); if (tool) { resolved.push(tool.definition); } else { notFound.push(name); } } if (notFound.length > 0) { log.warn(`[Orchestrator] Tools not found: ${notFound.join(', ')}`); } return resolved; } // 未指定白名单 — 使用所有已启用工具(排除 delegate_task) return this.toolRegistry.listTools().filter((t) => !EXCLUDE_TOOLS.has(t.name)); } /** * 构建 SubAgent 的 System Prompt */ private buildSubAgentPrompt(description: string, depth: number): MetonaSystemPrompt { return { roleDefinition: `You are a SubAgent (delegation depth: ${depth}) executing a specific sub-task delegated by the parent Agent.\nYour goal is to complete the assigned task efficiently and return a clear, concise result.\nFocus only on the task at hand. Do not delegate further.`, outputConstraints: `Complete the task and provide a clear summary of your findings or actions.\nRespond in the same language as the task description.\nKeep your response focused and relevant — the parent Agent will use your result to continue its work.`, safetyGuidelines: `Do not access files outside the workspace.\nDo not execute dangerous commands.\nIf the task cannot be completed, explain why clearly.`, }; } /** * 中断指定子任务 */ abortTask(taskId: string): boolean { const handle = this.activeSubAgents.get(taskId); if (handle && handle.status === 'running') { handle.abort(); return true; } return false; } /** * 完成子任务(外部触发,保留接口兼容) */ completeTask(taskId: string, result: string, success: boolean): void { const handle = this.activeSubAgents.get(taskId); if (handle && handle.status === 'running') { handle.status = success ? 'completed' : 'error'; handle.result = { taskId, result, success, durationMs: 0, iterations: 0 }; this.activeSubAgents.delete(taskId); this.emit('taskCompleted', handle.result); } } getActiveAgentsStatus(): Array<{ taskId: string; status: string; description: string; depth: number }> { return Array.from(this.activeSubAgents.values()).map((a) => a.getStatus()); } /** * 中断所有子任务 */ abortAll(): void { for (const agent of this.activeSubAgents.values()) { agent.abort(); } this.activeSubAgents.clear(); this.sessionDepth.clear(); } }