本文基于 AgentScope Java v2 官方文档 分析总结,帮助理解两个核心 Agent 抽象的设计意图、能力边界和选型策略。
一、本质定位
官方原文 :
HarnessAgent是ReActAgent的一层薄包装,把长期运行 agent 必备的工程能力打包进单一 builder。
┌─────────────────────────────────────────────────────────┐
│ HarnessAgent │
│ ┌───────────────────────────────────────────────────┐ │
│ │ ReActAgent(核心推理引擎) │ │
│ │ 一次请求 → 推理 → 工具调用 → 回复 │ │
│ └───────────────────────────────────────────────────┘ │
│ + 工作区 + 会话持久化 + 长期记忆 + 压缩 │
│ + 子Agent编排 + 沙箱隔离 + 技能装配 + 计划模式 │
└─────────────────────────────────────────────────────────┘
一句话区别:
| 抽象 | 解决什么问题 |
|---|---|
| ReActAgent | 「一次请求 → 推理 → 工具 → 回复」 |
| HarnessAgent | 「下一轮怎么接着上一轮、上下文如何保持有界、多用户如何隔离、危险操作如何先 review 再执行、可复用能力如何沉淀」 |
二、架构层面三大核心差异
2.1 能力是「叠加」而非「改写」
ReActAgent 的核心算法(推理-行动循环)没有被修改 ,Harness 只是在 ReAct 循环的关键时机通过 middleware 注入额外能力:
ReActAgent 主循环:
输入 → 推理 → LLM调用 → 工具调用 → 结果 → 循环/返回
Harness 叠加后:
输入 → [工作区注入] → 推理 → [压缩检查] → LLM调用
→ [权限审查] → 工具调用 → [沙箱执行] → 结果
→ [记忆沉淀] → 循环/返回
2.2 三个共享对象实现能力解耦
ReActAgent 是无状态的(除非手动配 Session),而 Harness 通过三个共享对象让所有能力互不依赖地协作:
| 共享对象 | 作用 | 持久化 |
|---|---|---|
RuntimeContext |
当前调用身份:sessionId、userId、自定义 extra |
❌ 不持久化,per-call |
| 工作区(Workspace) | 谁读写哪些文件,落盘到哪由配置决定 | ✅ 持久化 |
Session |
跨调用恢复运行时状态 | ✅ 持久化 |
2.3 Middleware 注册顺序
Harness 在构建期按固定顺序 串起所有内置 middleware,用户通过 .middleware(...) 加的自定义 middleware 跑在所有 Harness 内置之前。
三、能力对比清单
| 能力 | ReActAgent | HarnessAgent | 说明 |
|---|---|---|---|
| 推理-行动循环 | ✅ | ✅ | Harness 直接复用 |
| 工具调用(Tool / MCP) | ✅ | ✅ | 两者一致 |
| 权限系统 | ✅ | ✅ | 两者一致 |
| 人机交互(HITL) | ✅ | ✅ | 两者一致 |
| 流式输出(streamEvents) | ✅ | ✅ | 两者一致 |
| Middleware 钩子 | ✅ | ✅ | Harness 多了内置链路 |
| Session 持久化 | ⚠️ 需手动配置 | ✅ 默认开启 | ReActAgent 需 .session() 手动配 |
| 工作区驱动人格 | ❌ | ✅ .workspace(path) |
AGENTS.md / MEMORY.md / skills/ / tools.json |
| 双层长期记忆 | ❌ | ✅ .compaction(...) |
事实自动沉淀到 MEMORY.md |
| 对话压缩 | ❌ | ✅ .compaction(...) |
上下文有界,溢出时强制重试 |
| 大工具结果卸载 | ❌ | ✅ .toolResultEviction(...) |
超 80K 字符落盘 + 占位符 |
| 子 Agent 编排 | ❌ | ✅ .subagent(...) |
同步/后台,自动反向通知 |
| 可插拔文件系统 | ❌ | ✅ .filesystem(...) |
本机/共享存储/沙箱无缝切换 |
| 沙箱隔离 | ❌ | ✅ .filesystem(new DockerFilesystemSpec()...) |
Docker 级文件与命令隔离 |
| 计划模式(Plan Mode) | ❌ | ✅ .enablePlanMode() |
只读思考 + HITL 退出 |
| 技能装配 | ❌ | ✅ .skillRepository(...) |
Git/Nacos/MySQL/classpath/工作区 |
| MCP 工具白名单 | ❌ | ✅ workspace/tools.json |
声明式配置允许/拒绝 |
四、状态管理差异
ReActAgent 只有调用内状态 ,而 Harness 有三层状态自动流转:
┌──────────────────────────────────────────────────────┐
│ ReActAgent: 仅调用内状态 │
│ │
│ AgentState (对话上下文 + 权限 + 工具状态) │
│ RuntimeContext (sessionId, userId) │
│ → 调用结束即丢失(除非手动配 Session) │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ HarnessAgent: 三层状态自动流转 │
│ │
│ 第一层: 调用内状态 │
│ AgentState + RuntimeContext │
│ │
│ 第二层: 跨调用状态(自动持久化) │
│ agents/<agentId>/context/<sessionId>/ │
│ sessions/<sessionId>.log.jsonl (永不压缩) │
│ 子任务记录、沙箱元数据 │
│ │
│ 第三层: 长期记忆(跨 Session 累积) │
│ memory/YYYY-MM-DD.md (只追加) │
│ MEMORY.md (周期合并,注入 system prompt) │
└──────────────────────────────────────────────────────┘
三个值得记住的规律:
- system prompt 每轮重新拼 --- 修改
AGENTS.md或MEMORY.md立刻生效,无需重启 - 压缩/记忆提炼/后台维护都有节流闸门 --- 不会每轮都跑,避免性能损耗
AgentState由 core 的ReActAgent+Session自动持久化 --- Harness 不再额外做这件事
五、Maven 依赖差异
xml
<!-- 只用 ReActAgent:轻量 -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-core</artifactId>
<version>2.0.0-RC1</version>
</dependency>
<!-- 用 HarnessAgent:包含 core + 全部工程能力 -->
<dependency>
<groupId>io.agentscope</groupId>
<artifactId>agentscope-harness</artifactId>
<version>2.0.0-RC1</version>
</dependency>
六、典型代码对比
ReActAgent --- 轻量单次调用
java
ReActAgent agent = ReActAgent.builder()
.name("simple_agent")
.sysPrompt("你是一个有帮助的助手。")
.model("dashscope:qwen-plus")
.toolkit(new Toolkit())
.build();
Msg result = agent.call(List.of(new UserMessage("你好"))).block();
HarnessAgent --- 生产级多轮对话
java
HarnessAgent agent = HarnessAgent.builder()
.name("enterprise-assistant")
.sysPrompt("你是一个企业级助手。")
.model("dashscope:qwen-plus")
.workspace(Paths.get(".agentscope/workspace"))
.compaction(CompactionConfig.builder()
.triggerMessages(30)
.keepMessages(10)
.build())
.filesystem(new DockerFilesystemSpec()...)
.enablePlanMode()
.build();
RuntimeContext ctx = RuntimeContext.builder()
.sessionId("user-123-session-456")
.userId("alice")
.build();
agent.call(new UserMessage("帮我分析上周的销售数据"), ctx).block();
七、场景选择指南
适合 ReActAgent 的场景
| 场景 | 原因 |
|---|---|
| 无状态工具调用 | 单次请求-响应,不需要记住上一轮 |
| 快速原型 / PoC | 最少配置,快速验证想法 |
| 嵌入式 Agent | 作为 Pipeline / Workflow 中的一个节点 |
| 自定义 Agent 编排 | 自己实现多 Agent、状态管理、记忆等 |
| 轻量级微服务 | 对依赖体积敏感 |
| 单元测试 | 配合 InMemorySession 做单元测试 |
| 简单 Chatbot | 一问一答,无需持久化 |
适合 HarnessAgent 的场景
| 场景 | 使用的 Harness 能力 |
|---|---|
| 多轮长期对话 | Session 自动持久化,sessionId 不变即恢复 |
| 多用户 / 多租户 | RuntimeContext 天然隔离 userId / sessionId |
| 需要记忆积累 | 双层记忆:对话压缩 + MEMORY.md 长期事实沉淀 |
| 生产部署 | 沙箱隔离、文件系统抽象、Redis/MySQL Session |
| 子 Agent 编排 | 声明式子 Agent,支持同步/后台/流式 |
| 技能复用 | Git/Nacos/MySQL 技能仓库,可跨 Agent 共享 |
| 安全敏感操作 | Plan Mode 先思考再执行、HITL 审批、权限白名单 |
| 代码生成 / Coding Agent | 沙箱隔离执行、文件系统抽象 |
| 企业级 AI 助手 | 工作区驱动人格、MCP 白名单、记忆系统全套 |
八、决策流程图
你的 Agent 需要什么?
│
├─ 只需要一次问答 ──────────────────────→ ReActAgent
│
├─ 需要多轮对话但无持久化需求 ───────────→ ReActAgent + InMemorySession
│
├─ 需要会话持久化 ──────────────────────→ ReActAgent + JsonSession/RedisSession
│ (手动配置 session)
│
├─ 需要长期记忆 + 对话压缩 ─────────────→ HarnessAgent (.compaction)
│
├─ 需要工作区驱动人格/技能 ─────────────→ HarnessAgent (.workspace)
│
├─ 需要子Agent编排 ─────────────────────→ HarnessAgent (.subagent)
│
├─ 需要沙箱隔离 ────────────────────────→ HarnessAgent (.filesystem + Docker)
│
├─ 需要多用户隔离 + 生产部署 ────────────→ HarnessAgent(全套)
│
└─ 需要以上多个能力的组合 ──────────────→ HarnessAgent(按需开启)
九、总结
| 维度 | ReActAgent | HarnessAgent |
|---|---|---|
| 类比 | 发动机 | 整辆汽车(发动机 + 底盘 + 车身) |
| 关注点 | 推理质量、工具准确性 | 工程完备性、长期运行、生产就绪 |
| 复杂度 | 低,几行代码启动 | 中,需要理解工作区目录结构 |
| 可扩展性 | 通过 Middleware 自由扩展 | 内置能力丰富,也支持自定义 Middleware |
| 学习路径 | 先学 ReActAgent → 再学 Harness | 推荐:直接从 Harness 快速开始 |
| 适用阶段 | 原型验证、学习理解 | 生产部署、企业应用 |
官方推荐路径 :先用 HarnessAgent 快速开始(依赖 agentscope-harness 即可),理解工作区、记忆、会话等概念后,再根据实际需要决定是否降级到裸 ReActAgent 做更精细的控制。