17-skill-orchestration-and-concurrency.md 5.06 KB

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 选项,系统可以从一次输出流中正则匹配出所有的调度请求:

[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)

使用字典格式响应式地保存所有的子任务对象:

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 的反馈下文继续下一轮迭代。