我原以为
swarm_init会启动三个 Agent。亲手对 Ruflo 的 MCP Server 发完请求后,模型调用数是 0:它创建的是一条持久化的 swarm 记录。再调用agent_spawn,返回值甚至主动提醒我——这一步只是注册 Agent;真正执行还要走agent_execute、Claude Code 的 Task 工具或claude -p。这不是一个实现瑕疵,而是理解 Ruflo 的钥匙:它首先是控制面,其次才是执行器。
本文基于 Ruflo 主干提交 277c7bc(2026-09-05)和 npm 发布包 ruflo@3.38.21。我读了当前 CLI、MCP、Hooks、Swarm、Memory 与 Agent 执行链,并在空目录里做了一次最小 JSON-RPC 实验。
先给结论:
Ruflo 不是“比 Claude Code / Codex 更聪明的 Agent”,而是一个 Agent 元 Harness:它不接管宿主的全部推理循环,而是用配置注入、MCP 工具、持久化状态和反馈 Hooks,把原本一次性、彼此失联的 Agent 调用组织成可路由、可追踪、可复用的长期系统。
Ruflo 之所以必须长成“外置控制面 + 多个执行后端 + 持久化反馈回路”,是因为它不拥有 Claude Code / Codex 的内部循环,也不能假设一个模型进程永远活着。假如这个约束不存在,它完全可以做成一个封闭的 Agent Runtime;正因为宿主和生命周期不归它管,它才需要从外面注入规则、暴露工具、保存状态,再把结果喂回下一次决策。
名词速查
| 术语 | 一句话解释 |
|---|---|
| Harness | 包在模型外面的运行系统,负责工具、上下文、循环、权限和验证;站内已有完整定义:《什么才是好的 Harness》 |
| 元 Harness | 不只服务一个 Agent,而是给 Claude Code、Codex 等现成 Harness 再加一层协调、记忆与治理 |
| MCP | Model Context Protocol,把外部能力以标准工具协议暴露给模型宿主 |
| Swarm | 多个 Agent 围绕同一目标协作;Ruflo 里既指拓扑与状态,也可能指真正执行中的 Agent 集合 |
| 控制面 / 数据面 | 控制面决定“谁、何时、按什么规则做”;数据面真正执行模型调用、代码修改或外部操作 |
| ReasoningBank | 保存成功与失败轨迹、供后续检索和路由使用的经验库思路 |
| HNSW | 一种近似向量检索索引,用速度换少量精确度,适合从大量记忆里找语义相近项 |
先拿到地图:五个活动部件
把这个大仓库压缩成一张地图,只需要盯住五个部件:
| 部件 | 它保存或决定什么 | 拨动它会怎样 | 坏掉会怎样 |
|---|---|---|---|
| 安装与注入 | MCP 配置、Hooks、Skills、Agents、项目规则 | 注入越多,宿主越主动使用 Ruflo | 代码装了,宿主却不知道何时调用它 |
| MCP 工具面 | 把路由、记忆、Swarm、策略等能力变成工具 | 工具越宽,能力更全,但发现和选择成本更高 | 能力存在于包里,却到不了模型手上 |
| 协调状态 | Swarm 拓扑、Agent 注册表、任务与健康状态 | 从一次性子任务升级为跨调用协作 | “创建了蜂群”只剩一句提示词,没有可观察事实 |
| 执行后端 | 直连模型 API、宿主 Task 工具、claude -p 子进程 | 决定谁真正花 token、改文件、返回结果 | 控制面看起来繁忙,实际没有任何工作发生 |
| 记忆与学习 | 历史结果、向量记忆、路由反馈与策略状态 | 用得越久,路由有机会从静态规则变成经验驱动 | 每次任务都像第一次见,错误重复交学费 |
flowchart LR
U[用户任务] --> H[Claude Code / Codex]
subgraph R[Ruflo 元 Harness]
I[安装与规则注入]
M[MCP 工具面]
C[Swarm / Agent 控制状态]
X[执行适配器]
L[Memory / Learning]
I --> H
H --> M
M --> C
C --> X
X --> L
L --> M
end
X --> P1[模型 API]
X --> P2[宿主原生 Task]
X --> P3[Claude Code 子进程]
P1 --> O[结果与工件]
P2 --> O
P3 --> O
O --> L
这张图里最重要的不是箭头数量,而是边界:Claude Code / Codex 仍然是主循环的宿主,Ruflo 从外部提供控制能力;Swarm 记录和真正执行也不是同一个动作。
它的共同祖先因此不是“聊天机器人框架”,而是更老的 control plane / data plane 分层。Kubernetes 的 API Server 可以登记三个 Pod 的期望状态,但真正拉镜像、启动进程的是节点上的执行器。Ruflo 的 swarm_init 与 agent_spawn 也先建立可持久化的协调事实,再由另一条执行路径兑现它。
第一层:ruflo 先把自己接到宿主身上
先看一个容易误判的仓库细节。当前 ruflo 可执行文件本身非常薄:ruflo/bin/ruflo.js 主要负责 Ruflo 品牌、版本快速返回和运行模式判断,随后把工作委托给 @claude-flow/cli。仓库根包仍叫 claude-flow,MCP Server 对外报告的内部版本仍是 3.0.0,而 npm 包版本已经是 3.38.21。
这说明 Ruflo 不是一次干净重写,而是 Claude Flow 演化、改名和模块化后的连续系统。读源码时如果只盯 ruflo/ 目录,会错过真正的 CLI 主体;看到多个 MCP、Swarm 实现,也不能默认它们都在当前发布路径上。
真正关键的是 init。executeInit() 依次创建目录、生成 settings.json 与 .mcp.json、复制 Skills / Commands / Agents、写入 Helpers、Statusline、运行时配置和 CLAUDE.md,并为非纯内存模式提前创建持久化 Memory DB。
这里的设计比“安装一些文件”更深一层:Ruflo 必须让宿主在正确时机想起它。
例如 generateHooksConfig() 会把命令检查接到 PreToolUse,把结果记录接到 PostToolUse,把智能路由接到 UserPromptSubmit。生成的 CLAUDE.md 还明确写下“任务前搜索记忆、任务后保存成功模式”的操作约定。
所以第一层不是 Runtime,而是行为注入层:
包已经安装
≠ 宿主会使用它
MCP 已注册 + Hooks 已挂载 + 项目规则已生成
≈ 宿主在任务生命周期里有机会主动调用它
这也解释了插件安装与完整 CLI 初始化为什么不是一回事。前者可以只给几个命令和 Agent 定义;后者才把 MCP、Hooks、Memory 和项目级规则接成闭环。
第二层:MCP 是端口,不是大脑
Ruflo 用 MCP 把内部模块投影成宿主可调用的工具。当前发布包的 stdio Server 会读取逐行 JSON-RPC,请求过大时用 10 MB 上限拒绝;它还把普通日志重定向到 stderr,避免一行非 JSON 输出污染协议流。这些实现可以在 startStdioServer() 里看到。
我对 ruflo@3.38.21 实际发送 initialize 与 tools/list,得到 333 个工具,其中包含:
agent_spawn agent_execute
swarm_init swarm_status
hooks_route hooks_post-task
memory_store memory_search
policy_evaluate workflow_create
333 不是“模型一次要记住 333 个工具”的同义词。代码支持按工具名、类别或前缀筛选对外暴露的工具;项目文档也鼓励先用工具搜索再按需发现。这恰好暴露了工具平台的核心矛盾:能力面越宽,发现成本与上下文负担越高。
一条工具调用进入本地 Registry 后,会经过一个很清楚的夹心结构:
decision = authorize(toolName, input)
if (decision !== "allowed") throw
result = tool.handler(input)
return scanToolOutput(result)
这是对 callMCPTool() 的压缩:调用前统一过策略,调用后统一过内容边界。 把横切能力放在唯一窄口,而不是让 333 个 Handler 各写一遍,是 Ruflo 最值得复用的架构思路之一。
但这里必须泼一杯冷水:有窄口不等于默认强制安全。策略引擎的兼容默认值是 legacy;工具输出的严格 Guardrail 只有 CLAUDE_FLOW_STRICT_GUARDRAIL=true 时才启用,而且安全模块缺失时会 fail open。源码位置分别在 policy-runtime.ts 与 mcp-client.ts。
因此“具备策略能力”和“当前安装正在强制策略”必须分开检查。安全功能出现在功能表里,不代表它已经替你完成安全配置。
第三层:Swarm 先建立协调事实,再谈执行
这是我读源码时最重要的一次纠偏。
swarm_init 会校验拓扑与 Agent 上限,生成 swarmId,然后保存下面这类状态:
{
"topology": "hierarchical",
"maxAgents": 3,
"status": "running",
"agents": [],
"tasks": [],
"config": {
"strategy": "specialized",
"communicationProtocol": "message-bus",
"consensusMechanism": "majority"
}
}
注意:这里没有模型调用,也没有 spawn() 子进程。它创建的是控制状态。
接着看 agent_spawn:它校验输入、选择模型、写 Agent Registry、把 Agent ID 挂进最近的 Swarm,并尽力写图数据库或创建隔离 Memory 分支。返回状态是 registered,源码和实测返回值都明确列出三条真正执行路径:
agent_execute:按 Agent 记录里的模型与 Provider 直接调用 LLM API;- Claude Code 的 Task 工具:让宿主派生真实子 Agent;
claude -p:启动 headless Claude Code 子进程。
我在空目录里把 swarm_init(topology="hierarchical", maxAgents=3) 与 agent_spawn(agentType="researcher") 连起来跑了一遍。结果是:
| 观察项 | 实际结果 |
|---|---|
| Swarm | 写入一条 running 记录,maxAgents=3 |
| Agent | 写入 Registry,并进入 Swarm 的 agents 数组 |
| 模型选择 | 路由到 opus,Provider 为 anthropic |
| 模型请求 | 没有发生 |
| Agent 真正状态 | registered,等待另一条执行路径 |
这个分离并不虚。相反,它允许 Ruflo 用同一套控制状态接不同执行面:今天可以直连 Provider,明天可以借宿主原生 Agent,后台任务可以交给子进程,甚至还能接 WASM 或远程联邦执行器。
它的代价是命名会制造错觉。spawn 在很多系统里意味着进程已经启动,在这里主要意味着“登记一个可执行身份”。如果监控界面把“已登记”画成“正在工作”,你会得到一个看似繁忙、实则零 token 的蜂群。
第四层:路由不是一次分类,而是一个可退化的决策梯子
Ruflo 的任务路由不是押注单一算法。当前 hooks_route 大致按下面的顺序尝试:
AgentDB / LearningSystem 历史路由
↓ 不可用或置信度不足
原生 HNSW 语义路由
↓ 不可用
纯 JavaScript 余弦相似度
↓ 没有合格匹配
关键词规则
这是一条由贵到便宜、由经验到静态、允许降级的决策梯子。系统不会因为向量库没装好就失去全部路由能力。
我刻意关闭语义路由,用任务 research Ruflo architecture and review security 测试关键词兜底。返回结果把 security-architect 作为主 Agent,置信度 0.92,备选是 security-auditor 与 reviewer,并建议三 Agent 的层级式 Swarm。
这个结果能证明“兜底链跑通”,不能证明 0.92 真等于 92% 成功率。代码里的置信度首先是规则分数,不是经过线上校准的概率。这是读此类系统最该保持的敏感度:字段叫 probability,不代表它已经通过概率校准。
如果路由最终选择真实执行,agent_execute 会读取 Agent 的模型配置,经 Provider Router 调用 Anthropic、OpenRouter 或 Ollama;对 429、5xx 与超时等可重试错误,它还能在有候选模型时做有界回退。成功或失败随后会更新 Agent 状态与路由先验。真实主干在 executeAgentTask()。
这里最值得学的不是某个路由算法,而是三条工程纪律:
- 降级链要保留最低可用能力:向量检索坏了,关键词仍能给结果;
- 重试必须有预算:默认回退次数有限,避免 Provider 故障变成重试风暴;
- 选择与结果要能配对:只记录“当时选了谁”学不到东西,必须再写回“后来成没成”。
第五层:Memory 不是聊天记录,而是下一次决策的输入
Ruflo 的 Memory 层试图同时承接两种查询:
- 精确键、前缀、标签等结构化查询交给 SQLite;
- 语义相似查询交给 AgentDB / 向量索引;
- Hybrid 查询把两类结果组合起来。
这条路由在 HybridBackend.query() 里写得很直白。它像图书馆同时保留“按书号取书”和“按意思找书”:前者确定、便宜;后者能接住“我记得大意,不记得名字”。
真正形成闭环的是 Post-Task Hook。hooks_post-task 会接收任务、Agent、成功与否、质量、耗时等信号,然后尽力完成几类写入:
flowchart LR
T[任务完成] --> F[结果反馈]
F --> A[更新 Agent 生命周期]
F --> R[保存路由结果]
F --> G[写因果 / 强化关系]
F --> P[更新模式与轨迹]
R --> N[下一次 hooks_route]
G --> N
P --> N
这就是 README 里 “self-learning” 最可信的工程解释:不是模型权重自动神奇变强,而是系统把历史结果物化成可检索记录、统计先验和路由信号,下一次选择时再利用。
不过反馈质量仍是整条链的软肋。源码自己承认,agent_execute 目前把“API 正常返回”当作最弱的成功代理,它不等于代码正确,更不等于用户接受。如果把这种信号无差别写回,学习闭环会把“答得出来”误当成“做对了”。
所以自学习系统的上限通常不在向量库,而在 verifier:谁来确认测试通过、工件可用、用户接受?没有硬验证器,反馈回路只是把主观评分保存得更勤快。
一次任务到底怎么流动
把前面的部件合起来,一次完整任务可以这样走:
sequenceDiagram
participant U as 用户
participant H as Claude Code / Codex
participant R as Ruflo MCP
participant S as Swarm / Agent Store
participant E as 执行后端
participant M as Memory / Learning
U->>H: 提交任务
H->>R: hooks_route(task)
R->>M: 检索历史模式
M-->>R: 相似经验 / 无结果
R-->>H: Agent、模型与拓扑建议
H->>R: swarm_init + agent_spawn
R->>S: 保存协调状态
H->>R: agent_execute 或调用原生 Task
R->>E: 模型 API / 子进程 / 宿主 Agent
E-->>R: 输出、耗时、错误
R->>M: hooks_post-task(feedback)
M-->>R: 更新轨迹与路由先验
R-->>H: 结果与可观察状态
H-->>U: 最终交付
注意其中两处“建议”而非“命令”:Ruflo 可以推荐 Agent、模型与拓扑,但宿主模型是否调用这些工具,仍受项目规则、工具可见性与宿主判断影响;如果走 Claude Code Task,真正的子 Agent 生命周期仍由 Claude Code 掌握。
所以 Ruflo 不是把宿主吃掉,而是在宿主周围长出一层可编程的组织能力。
它真正有价值的三个思路
1. 把经验从提示词搬成外部状态
提示词里的“上次这个任务应该找安全专家”会随上下文消失;写进路由结果、Pattern Store 和 Memory 后,经验才有跨会话复用的可能。这个方向和站内 《MCP 状态不会消失,只会搬家》 是同一个问题:状态不能靠进程记忆碰运气,必须有明确归属和生命周期。
2. 把执行器做成可替换端口
同一个 Agent 身份可以接直连模型、宿主 Task 或 headless 进程。控制面不需要押注单一 Provider,也不需要假装自己拥有宿主内部 API。这种边界让系统更杂,却也让迁移成本更低。
3. 把横切规则收敛到窄口
工具调用统一经过策略引擎与结果扫描,任务完成统一经过 Post-Task 反馈,向量检索失败统一走降级链。横切能力只有在窄口上才可能被审计;散在几百个 Handler 里,只会变成无法证明的一致性愿望。
它目前最需要警惕的四个代价
1. 表面面积过大
333 个 MCP 工具、数十个插件、CLI、Hooks、Daemon、Memory、Federation、Web UI 同时存在。能力很多是真的,但可理解性本身已经成为稀缺资源。如果没有工具过滤和渐进发现,工具面会反过来污染模型上下文。
2. 控制状态容易被误认成执行状态
running 的 Swarm 可能只是持久化记录,registered 的 Agent 可能还没发出一次模型请求。生产监控必须区分 desired / registered / dispatched / running / verified,而不能用一个绿色圆点包办。
3. 演化中的多实现会增加认知税
仓库同时保留顶层 v3/mcp、@claude-flow/mcp、CLI 内置 MCP Tools,以及多套 Swarm 抽象。它们有的是当前发布路径,有的是库实现或迁移遗产。读者和贡献者都要先回答“谁在调用它”,再谈实现细节。
4. 学习信号不够硬
API 成功、规则置信度、人工传入的 quality 都可以当信号,但都不是任务正确性的天然证明。Ruflo 已经把反馈管道接得很长,下一阶段最值钱的不是再加一种 Memory,而是把测试、评测器、用户接受与回归检测接成更硬的结果标签。
什么时候值得用,什么时候先别用
| 场景 | 我的判断 | 理由 |
|---|---|---|
| 一次性的小改动、单个子任务 | 先用宿主原生 Agent | Ruflo 自己的工具描述也承认,没学习与协调需求时原生 Task 更直接 |
| 多阶段研发,需要跨会话记忆 | 值得试 | 外置状态、Hooks 与结果回写开始产生复利 |
| 多 Provider、成本路由与故障回退 | 值得试,但要实测 | 执行端口和降级链有真实代码;路由分数未必等于校准后的收益 |
| 需要可审计的多 Agent 协作 | 有潜力,先配置 enforce | 策略窄口存在,但兼容默认值不是强制安全 |
| 只因为“Swarm 听起来更强” | 不建议 | Agent 数量不是收益,协调税才是确定成本;参见 Agent Swarm 深读 |
| 团队还没有可靠测试与交付门 | 先补 verifier | 没有硬结果信号,学习回路只会放大含糊反馈 |
一句话收口
Ruflo 的本质不是“替你启动很多 Agent”,而是给现成 Agent Harness 外挂一个控制面:安装时注入能力,任务前检索与路由,运行中登记与执行,任务后把结果写回记忆。
最朴素的“每次开几个独立 Agent、靠提示词让它们合作”先崩在跨会话状态和可观察性;Ruflo 用 Registry、Hooks、MCP 与 Memory 接住了这个崩点。它目前的风险则在另一端:控制面的广度已经远超多数项目的需要,而“记录成功”还没有天然等于“验证正确”。
如果只想花 5 分钟验证本文,不必安装完整项目:
- 运行
npx -y ruflo@latest --version,确认当前发布包; - 打开
swarm-tools.ts的swarm_init,找它有没有启动模型或进程; - 再打开
agent-tools.ts的返回说明,看真正执行被分到了哪三条路径。
读完这两段,你就不会再被“100+ Agents”带偏:数量是产品表面,控制状态怎样变成可验证执行,才是 Ruflo 真正值得追的那条线。
参考来源
工程与源码
- Ruflo 仓库与 README
- Ruflo npm 包
- CLI 初始化执行器
- MCP stdio Server
- Swarm MCP Tools
- Agent MCP Tools
- Hooks 路由与反馈
- Hybrid Memory Backend
本文亲手验证
- 日期:2026-09-07;版本:
ruflo@3.38.21 - 方法:在空目录启动 stdio MCP Server,发送
initialize、tools/list、swarm_init、agent_spawn与关闭语义路由后的hooks_route - 覆盖:工具注册数、Swarm 持久化、Agent 注册、关键词路由;没有配置 Provider Key,因此没有发起真实模型请求,也没有评测输出质量