HarnessAgent vs ReActAgent 对比

在项目的性能测试中发现,spring-ai-alibaba(使用 ReActAgent)的平均响应时间(7213ms)比 agentscope(使用 HarnessAgent)快约 2.47 倍(17805ms)。

本文从源码层面深入分析两者的核心差异,以及对应的优化实践。

一、关系与定位

HarnessAgent
  └── 内部持有 ReActAgent delegate(真正执行 LLM ReAct 循环)
       └── build() 时向 ReActAgent 注入 N 层 Middleware

HarnessAgent 并不是 ReActAgent 的替代品,而是包裹它的高层编排框架HarnessAgent 的 Javadoc 原文:

HarnessAgent is the user-facing harness API that wraps a ReActAgent with workspace / filesystem / sandbox / subagent / skill / plan-mode / MCP orchestration.

For plain ReAct usage without any of the above, use ReActAgent#builder() directly.

二、Middleware 装配差异

这是两者性能差距的根本原因。每次调用 HarnessAgent.builder()...build() 时,harness 向内部 ReActAgent 注入约 12+ 层 Middleware

Middleware 功能 HarnessAgent ReActAgent
WorkspaceContextMiddleware 读取 AGENTS.md / MEMORY.md / KNOWLEDGE.md 注入上下文
AtPathExpansionMiddleware @path 语法自动展开为文件内容
MemoryFlushMiddleware 超长上下文时将历史消息落盘到 workspace
MemoryMaintenanceMiddleware 对话历史维护与 context 管理
CompactionMiddleware token 超限时自动压缩历史(本项目已 disableCompaction()
ToolResultEvictionMiddleware 清除过旧的 Tool 执行结果
HarnessSkillMiddleware 按需从 AgentSkillRepository 动态加载 skill code 并执行
SandboxLifecycleMiddleware Docker 沙箱容器生命周期管理
SubagentsMiddleware Subagent 编排(task / task_output 工具)
AsyncToolMiddleware + InboxMiddleware 异步 Tool 执行与消息收件箱
PlanModeMiddleware Plan 模式(只读设计阶段)
SkillUsageMiddleware + SkillCuratorMiddleware Skill 自学习与自动提升
AgentTraceMiddleware 执行链路追踪

ReActAgent.build() 仅做轻量装配(Toolkit 复制、Hook 注册、Middleware 排序),不含上述任何层。

源码佐证

// HarnessAgent.java Builder.build() — 部分 middleware 注册片段
inner.middleware(new AgentTraceMiddleware());
inner.middleware(new WorkspaceContextMiddleware(...));
inner.middleware(new AtPathExpansionMiddleware(wsManager));
inner.middleware(new MemoryFlushMiddleware(...));
inner.middleware(new MemoryMaintenanceMiddleware(...));
inner.middleware(new CompactionMiddleware(...));       // disableCompaction() 可跳过
inner.middleware(new ToolResultEvictionMiddleware(...));
inner.middleware(new InboxMiddleware(...));
inner.middleware(new SubagentsMiddleware(...));
inner.middleware(new AsyncToolMiddleware(...));
inner.middleware(new HarnessSkillMiddleware(...));     // Skill 核心
// + PlanModeMiddleware、SkillUsageMiddleware、SkillCuratorMiddleware ...
// ReActAgent.java Builder.build() — 核心装配(精简)
Toolkit agentToolkit = this.toolkit.copy();
if (skillBox != null) configureSkillBox(agentToolkit);
middlewares.sort(Comparator.comparingInt(MiddlewareBase::order).reversed());
return new ReActAgent(this, agentToolkit);

三、功能能力对比

四、实测性能对比

基于 mailanti-ai-antiagent 项目,300 题 × 3 服务的并发压测(seed = 42,并发 5 线程):

v1 基准测试(未优化)

服务 avg P50 P90 P95 max 成功率
spring-ali(ReActAgent) 7213ms 6890ms 10492ms 12031ms 16647ms 100%
agentscope(HarnessAgent) 17805ms 16173ms 26824ms 33724ms 89380ms 95.7%
倍率 2.47x 2.35x 2.56x 2.80x

spring-ali 在 285 对配对中,99.3% 的题目更快,平均领先 10639ms

性能差距的代码层根因

① 每次请求重建 HarnessAgentAgentscopeAgentChatProvider.java:55

try (HarnessAgent agent = agentFactory.create(command.getAgentHost(), command.getModel())) {
    // 每次请求都 build() + close(),12+ 层 Middleware 每次重新装配
}

② workspace 文件系统 I / OAgentFactory.java:43

HarnessAgent.builder()
    .workspace(Path.of(properties.getWorkspaceRoot(), normalizedHost))
    // build() 时 WorkspaceManager.validate() + WorkspaceIndex.open() 触发 I/O

③ 默认 JsonFileAgentStateStore

不传 stateStore 时,harness 使用 JsonFileAgentStateStore,每次 build 从 ~/.agentscope/state/<agentId>/ 读写 session 状态文件。

五、优化实践

优化 1:workspace 定时预热

新增 WorkspaceWarmupScheduler,启动时 + 每 20 分钟对所有 agentHost 执行一次 build+close 预热,提前完成 skill 文件落盘:

@Slf4j
@Component
@RequiredArgsConstructor
public class WorkspaceWarmupScheduler {

    private final AgentFactory agentFactory;
    private final AgentHostMapper agentHostMapper;
    private final SkillService skillService;

    @PostConstruct
    public void warmupOnStartup() {
        warmupAll();
    }

    @Scheduled(fixedDelay = 20 * 60 * 1000L, initialDelay = 20 * 60 * 1000L)
    public void warmupOnSchedule() {
        warmupAll();
    }

    private void warmupAll() {
        List<AgentHostEntity> hosts = agentHostMapper.selectList(
                new LambdaQueryWrapper<AgentHostEntity>().eq(AgentHostEntity::getEnabled, 1));
        for (AgentHostEntity host : hosts) {
            String normalizedHost = skillService.normalizeAgentHost(host.getAgentHost());
            try {
                agentFactory.preWarmWorkspace(normalizedHost);
            } catch (Exception ignored) {}
        }
    }
}

优化 2:InMemoryAgentStateStore 缓存

将默认文件持久化改为按 agentHost 缓存的纯内存实现,InMemoryAgentStateStore 内部已按 (userId, sessionId) 隔离:

// AgentFactory.java
private final ConcurrentHashMap<String, InMemoryAgentStateStore> stateStores = new ConcurrentHashMap<>();

private HarnessAgent buildAgent(String normalizedHost, String modelName) {
    // ...
    InMemoryAgentStateStore stateStore = stateStores.computeIfAbsent(
            normalizedHost, k -> new InMemoryAgentStateStore()
    );
    return HarnessAgent.builder()
            .name("agentscope-" + normalizedHost)
            .model(model)
            .workspace(Path.of(properties.getWorkspaceRoot(), normalizedHost))
            .skillRepository(repo)
            .stateStore(stateStore)   // 纯内存,零文件 I/O
            .disableCompaction()
            .build();
}

优化效果(v1 → v3.0)

指标 v1(优化前) v3.0(优化后) 变化
avg 17805ms 15938ms ↓1867ms(-10.5%)
P50 16173ms 13328ms ↓2845ms(-17.6%)
P75 21613ms 19158ms ↓2455ms(-11.4%)
P95 33724ms 31845ms ↓1879ms(-5.6%)
max 89380ms 61324ms ↓28056ms(-31.4%)
≤10s 占比 16.7% 27.4% ↑10.7pp
与 spring-ali 差值均值 10639ms 9098ms ↓1541ms(-14.5%)

P50 改善最显著(-17.6%),≤10s 快速响应比例大幅提升,说明两项优化在中位数附近效果最明显。

六、剩余差距分析

优化后 agentscope avg 15938ms,仍是 spring-ali(6845ms)的 2.33 倍

优先级 根因 状态 影响
P0 HarnessAgent middleware 装配链(12+ 层) ❌ 未消除 高(主因)
P1 workspace WorkspaceManager.validate() 轻量 I / O ✅ 预热缓解
P2 JsonFileAgentStateStore 文件读写 ✅ 内存化消除
P3 HarnessSkillMiddleware vs SkillBox 差异 ❌ 未优化

进一步优化方向

若需彻底消除差距,核心方案是将 HarnessAgent 换为 ReActAgent(参考 spring-ali 实现),只保留 SkillBox + InMemoryMemory,彻底跳过 12+ 层 harness Middleware 的装配开销,预期 avg 可降至 7-9s 量级。

该方案代价是放弃 HarnessAgent 提供的 workspace 文件上下文、Subagent 编排、Skill 自学习等高级能力 —— 需根据实际业务需求权衡取舍。

七、总结

使用 HarnessAgent 的场景:
  ✅ 需要 workspace 文件上下文(AGENTS.md 引导 Agent 行为)
  ✅ 需要 Skill 自学习(SkillCurator 自动提升常用 skill)
  ✅ 需要多 Agent 协作(Subagent 编排)
  ✅ 需要 Sandbox 代码执行(Docker 隔离)

使用 ReActAgent 的场景:
  ✅ 纯问答 / 工具调用 / RAG
  ✅ Skill 列表固定,无需动态加载
  ✅ 对延迟敏感,每请求开销要尽量小
  ✅ 代码更简洁,维护成本低

对于 mailanti-ai-antiagent 这类以单轮问答 + 固定 skill 调用为主的场景,ReActAgent 是更合适的选择。