# 17. Skill Orchestration & Concurrency Architecture 本文档记录了系统中关于 Skill 管理机制的演进思路,以及为支撑 Innovator 等 Orchestrator 高效执行多目标任务而设计的“并发子代理 (Concurrent Sub-Agent)”架构。我们正式废除了旧有的 SQLite 数据库绑定方案,确立了 **Local File-System First(纯文件系统挂载)** 原则。 --- ## 1. Local File-System First 架构 (移除 SQLite / data/skills 依赖) 在旧版本中,Skill 系统的可用状态、元数据及其层级关系被同时维护在 `data/skills/` 和本地 `skills.sqlite` 数据库里。这种双源结构经常导致: - 文件存在但由于数据库未记录,导致技能不可用 (幽灵 Bug)。 - 修改本地文件(如重写提示词)后热重载失效。 ### 1.1 核心重构与 `rootdir/skills/` 绝对源头 目前的纯无状态化架构中,取消了所有的 DB 查询(弃用 `server/services/skill-db` 参与核心运行时计算),直接对原生的目录系统进行实时挂载扫描: - 系统核心组件 `skill-config-loader.js` 会在每次拉取架构配置时,直接遍历项目根目录下的 `skills/`(即 `path.resolve(__dirname, '../../../skills')`)。 - 目录存在即技能存在;内部包含 `SKILL.md` 的文件夹将被全自动解析并作为可用 Agent 注入。如果该文件含有 `layer: orchestrator` 或类似的大脑定义,将自动提升为 Orchestrator。 - 这一改动**完全去除了状态机依赖**并发地保持了 Claude CLI 等底层协议提倡的原始文件驱动属性。 ### 1.2 注入点 当我们首次向 Innovator 等代理生成 `system prompt` 时,系统扫描出的 `fullSystemConfig.agents` 配置会实时序列化后填充到提示词中的 `{{AVAILABLE_AGENTS}}` 占位符内,让主代理在没有任何 DB 信息的前提下也能了解当前所有安装的可调用技能池。 --- ## 2. 并发子编排引擎 (Concurrent Orchestration Engine) 在研究深度报告 (Deep Research) 或网页浏览时,Innovator 通常需要发起对多个独立话题甚至工具的并行调用,以压缩墙上时间(Wall-clock time)。为了支撑该目标,系统对基础解析器(`parser.js`)以及对话驱动路由(`agent-chat.js`)做出了彻底改造。 ### 2.1 突破串行化拦截 传统的设计为了维持“因果链 (Causal Chain)”,使用了 `_applySerialConstraint` 强制截取 Agent 产生的最后一个 `[CALL: xxx]` 数据块。现在通过开放 `allowParallel: true` 选项,系统可以从一次输出流中正则匹配出所有的调度请求: ```text [CALL: meta_research] 分析 AI 的未来发展趋势 [/CALL] [CALL: meta_research] 调查自动驾驶汽车的采用率 [/CALL] ``` 以上将返回完整的 Calls Array。 ### 2.2 Promise.all 协程调度 在 `/api/agent/:agentId/chat` 核心路由中: - 将串行(`for (const call of calls)`)阻塞迭代替换为了 `await Promise.all()`。 - 这允许系统在一轮思考响应内,瞬间启动多条 `AgentManager.callAgent()` 协程链路。每一条链路都会实例化独立的底层 Claude 进程并发执行。 ### 2.3 任务追踪标识 (Unique Call ID) 为了对这些并发运行的环境进行隔离和精准回显追踪,路由在启动任务前会为每一个 Sub-Agent 事件分配一个唯一标识,例如: `const callId = "sub-" + randomUUID().substring(0, 6)` 向前端的 SSE 事件流中(如 `event: 'chunk'`)挂载该 ID,保证前端不论收到多混乱的异步流交错,都能正确路由进独立的终端。 --- ## 3. 前端多流终端并发 UI (Multi-Terminal UI) 由于传统的单窗口 `msg-assistant` 已经无法呈现多个异步进程在彼此互不相关的流速下喷吐的状态,为了匹配上述后端能力,并达成震撼的多线程交互体验,在 Vue 侧 (`AgentChat.vue`) 引入了**“多窗口动态并行卡片流 (Parallel Windows)”**: ### 3.1 状态驱动器 (`subAgents` Ref) 使用字典格式响应式地保存所有的子任务对象: ```javascript const subAgents = ref({ 'sub-1a2b3c': { agent: 'meta_research', status: 'running', content: '...' }, 'sub-9x8y7z': { agent: 'meta_digest', status: 'success', content: '...' } }); ``` 在收听 `sub_agent` 事件时,通过 `callId` 作为 Key 进行实时内容拼接和状态更新,甚至做到独立容器的自动 `scrollTop = scrollHeight` 下推。 ### 3.2 动态卡片墙 (Grid Flexbox) 在聊天气泡大外框的末端,通过 CSS Grid 自适应布局 `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` 渲染: - 当一个 Agent 并发出了三项分支,这三项任务会像控制台界面一般,在横向平整排列。 - 每一个子容器都拥用专属的 Terminal 等宽字体控制台、带有彩色指示灯(运行中蓝色辉光、完成绿勾、失败红叉)。 - 所有并行终端运行出各自的结果后(或者任意一条发生中断),后台等待在 `Promise.all` 的阻塞点会统一收束收集它们的汇总返回内容(包裹着 `[Sub Agent 返回结果]`),随后统一塞入主 Agent 的反馈下文继续下一轮迭代。