项目地址:https://github.com/agentscope-ai/agentscope-java
本文结合源码分析 HarnessAgent 如何在不修改 ReActAgent 代码的前提下,通过三种正交机制将 Core 引擎扩展为生产一站式解决方案。每个机制附带执行路径图和伪代码。
1. 架构总览:三种扩展机制
HarnessAgent 不继承 ReActAgent,而是通过组合 + 委托持有它。所有扩展通过三个正交机制实现,ReActAgent 的代码一行不改:
机制一:wrappedCall() --- 外层包装
├─ 沙箱生命周期(Mono.using acquire/release)
├─ 上下文溢出紧急恢复(onErrorResume + retry)
└─ RuntimeContext 富化(注入 SandboxContext/FS/WSManger)
机制二:中间件注入 -- Core 循环内的横切增强
├─ onSystemPrompt: WorkspaceContext, Skills
├─ onReasoning: Compaction, AtPathExpansion, Trace
├─ onActing: ToolResultEviction, PlanMode, Trace
└─ onAgent: MemoryFlush, MemoryMaintenance
机制三:工具注册 -- 让 Agent 能操作 Harness 基础设施
├─ FilesystemTool → AbstractFilesystem(多态后端)
├─ ShellExecuteTool → AbstractSandboxFilesystem(仅沙箱)
├─ MemorySearchTool/GetTool → WorkspaceManager
└─ PlanModeTools / SubagentTools
ReActAgent (Core) − 不被修改
├─ ReAct 循环 / 模型调用 / 工具执行 / 权限 / HITL / 状态
└─ 预留扩展点:5条中间件钩子 + Toolkit注册接口 + delegate模式
2. 机制一:外层 call () 包装
HarnessAgent.java line 731--757 (wrappedCall())
为什么需要外层包装?
有些行为不适合放在 ReActAgent 的中间件链里:
- 沙箱生命周期:必须在整个 call(包括所有中间件)的前后管理资源,放在 onAgent 链里的话,链之前的逻辑就无法使用沙箱
- 溢出恢复:需要
onErrorResume+ 重试整个 call,超出了中间件 "围绕某个阶段做增强" 的能力 - RuntimeContext 富化:需要在调用 delegate 前往上下文中塞入 Harness 专属对象
执行路径
HarnessAgent.call(msg, runtimeContext)
├─ ensureSessionDefaults(runtimeContext)
│ ├─ 补充默认 sessionId (fallback agent name)
│ ├─ put(SandboxContext.class, defaultSandboxContext)
│ ├─ put(AbstractFilesystem.class, wsManager.getFilesystem())
│ ├─ put(WorkspaceManager.class, wsManager)
│ └─ put(WorkspacePathNormalizer.class, pathNormalizer)
│ ← enriched RuntimeContext
└─ wrappedCall(msg, enrichedCtx, () → delegate.call(msg, enrichedCtx))
├─【层1】Mono.using − 沙箱生命周期
│ resourceInit:
│ sandboxLifecycleMw.acquireForCall(ctx)
│ ├─ SandboxManager.acquire()
│ ├─ sandbox.start()
│ └─ filesystemProxy.setSandbox(sandbox) 注入虚拟沙箱
│ inner.get():
│ delegate.call(msg, enrichedCtx)
│ └─ reasoning → acting → iterate...
│ (中间件链在此内部运行)
│ resourceCleanup:
│ sandboxLifecycleMw.releaseForCall(ctx)
│ ├─ persistState() 保存沙箱状态JSON
│ ├─ sandbox.stop() 持久化快照(tar归档)
│ ├─ sandbox.shutdown()
│ └─ filesystemProxy.setSandbox(null) 清除引用
├─【层2】onErrorResume − 上下文溢出紧急恢复
│ if compactionHook != null:
│ return base.onErrorResume(error => {
│ if isContextOverflowError(error): // 字符串匹配检测 "context_length_exceeded"
│ recoverFromOverflow(msg, ctx)
│ └─ forceCompactAndRetry() // 强制压缩对话{triggerMessages=1}
│ 重新调用 delegate.call() 用压缩后的上下文重试
│ return Mono.error(error) // 非溢出错误原样抛出
│ })
└─ return base
伪代码
wrappedCall(Msgs, ctx, innerSupplier):
// ===== 层1:沙箱生命周期 =====
// Mono.using(resourceInit, body, resourceCleanup)
// 保证 acquire 和 release 严格配对,即使 body 抛异常也会执行 cleanup
base = Mono.using({
init = () => {
if sandboxLifecycleMw != null:
sandboxLifecycleMw.acquireForCall(ctx)
// 内部: SandboxManager.acquire() → sandbox.start() → proxy.setSandbox()
},
body = () => innerSupplier.get(), // delegate.call(msgs, ctx)
cleanup = () => {
if sandboxLifecycleMw != null:
sandboxLifecycleMw.releaseForCall(ctx)
// 内部: persistState() → sandbox.stop() → sandbox.shutdown() → proxy.clear()
}
})
// ===== 层2:溢出恢复 =====
if compactionHook != null:
return base.onErrorResume(error => {
if isContextOverflowError(error): // 字符串匹配 "context_length_exceeded"
recoverFromOverflow(msgs, ctx)
// 强制压缩 − 重试整个 call
return base
})
return Mono.error(error) // 非溢出错误原样传播
return base
关键方法说明
| 方法 | 功能 |
|---|---|
| ensureSessionDefaults(ctx) | 往 RuntimeContext 中注入 SandboxContext、AbstractFilesystem、WorkspaceManager 等 Harness 对象 |
| wrappedCall(msgs, ctx, inner) | 外层包装入口,叠加沙箱生命周期和溢出恢复两层 |
| acquireForCall(ctx) | 获取 / 创建沙箱并注入到文件系统代理(被 Mono.using 的 init 调用) |
| releaseForCall(ctx) | 持久化状态、停止 / 销毁沙箱、清除代理引用(被 Mono.using 的 cleanup 调用) |
| isContextOverflowError(e) | 通过字符串匹配检测各种 LLM 提供商的上下文溢出错误信息 |
| recoverFromOverflow(msgs, ctx) | 强制压缩对话历史后重试整个 call |
| forceCompactAndRetry() | 以 triggerMessages=1 触发 ConversationCompactor,然后重新调用 delegate |
为什么不把沙箱做成 onAgent 中间件?
SandboxLifecycleMiddleware 虽然实现了 ,但没有重写任何钩子方法。它的 是被 直接调用的公开方法。 原因:
如果沙箱放在 onAgent 中间件链里:
onAgent_MW1 → onAgent_MW2 → ... → SandboxMW.onAgent → core
↑
此时沙箱才可用
但 MW1/MW2 可能需要访问文件系统(如 WorkspaceContextMiddleware 读 AGENTS.md)
此时沙箱还没启动,文件系统代理是空 → 报错
放在 wrappedCall() 的 Mono.using init 层:
acquireSandbox() → onAgent_MW1 → onAgent_MW2 → ... → core
↑
所有中间件执行时沙箱都已就绪 ✓
3. 机制二:中间件注入
HarnessAgent.java line 1904--2207 (build 中的注入)
ReActAgent 预留的 5 条子链
ReActAgent 在循环的各阶段通过 MiddlewareChain.build()
"洋葱模型"(Onion Model)是中间件架构中的一个经典比喻,指的是多层嵌套。由外向内再由内向外的执行顺序,就像洋葱一层套一层。每一层中间件包裹着内层请求,请求从最外层穿透到核心,响应再从核心向外返回。
| 钩子 | 触发时机 | 模式 | ReActAgent 源码位置 |
|---|---|---|---|
| onAgent | 包裹整个 agent 调用 | 洋葱 (before/after) | line 848 |
| onSystemPrompt | 系统提示词构建时 | 管线(顺序变换) | line 859 |
| onReasoning | 每次 LLM 推理调用前后 | 洋葱 | line 1888 |
| onModelCall | 每次原始 model.stream () 前后 | 洋葱 | line 2029 |
| onActing | 每次工具批量执行前后 | 洋葱 | line 2186 |
每个中间件只重写需要的钩子,未重写的默认传递 return next.apply(input)。
执行路径:13 个中间件在循环中的位置
delegate.call(msg, ctx)
┌─────────────────────────────────────────────────────┐
│ onAgent 链 (包裹整个调用) │
│ MemoryFlushMiddleware.onAgent │
│ → next.apply(input).doOnComplete(() => doFlush()) │
│ 调用完成后用 LLM提取记忆写入MEMORY.md │
│ MemoryMaintenanceMiddleware.onAgent │
│ → next.apply(input).doOnComplete(() => maintain()) │
│ 调用完成后检查是否需要归档/整合/清理 │
└─────────────────────────────────────────────────────┘
ReAct Loop
┌─────────────────────────────────────────────────────┐
│ onSystemPrompt 链(管线式追加) │
│ WorkspaceContextMiddleware │
│ → 追加 AGENTS.md /MEMORY.md/环境/日期 │
│ HarnessSkillMiddleware │
│ → 追加 <available_skills> 技能列表 │
└─────────────────────────────────────────────────────┘
reasoning(iter)
┌─────────────────────────────────────────────────────┐
│ onReasoning 链 │
│ CompactionMiddleware │
│ → 检查长度,超阈值则压缩后再传给next │
│ AtPathExpansionMiddleware │
│ → 展开用户消息中的 @path 引用 │
│ AgentTraceMiddleware │
│ → 记录推理日志 │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ onModelCall 链 │
│ AgentTraceMiddleware │
│ → 记录模型调用日志 │
│ └ model.stream() ←唯一LLM调用点 │
└─────────────────────────────────────────────────────┘
acting(iter)
┌─────────────────────────────────────────────────────┐
│ onActing 链 │
│ ToolResultEvictionMiddleware │
│ → 超大结果卸载到文件,替换为预览占位符 || │
│ PlanModeMiddleware │
│ → Plan Mode下拦截修改工具 │
│ AgentTraceMiddleware │
│ → 记录工具执行日志 │
│ SubagentsMiddleware │
│ → 推送已完成的后台任务结果 │
│ └ toolkit.callTools() 实际执行工具 │
└─────────────────────────────────────────────────────┘
executeIteration(iter+1)
四种中间件模式详解
Before 模式:CompactionMiddleware (onReasoning)
在 LLM 调用前变换输入,不影响后续流程。
CompactionMiddleware.onReasoning(agent, ctx, input, next):
messages = input.messages
systemMsg = messages[0] // 第一条是系统提示词
conversation = messages[1..] // 剩余是对话历史
compacted = ConversationCompactor.compactIfNeeded(conversation)
// 检查 token 是否超过阈值
// 超过 − 用 LLM 将消息摘要为摘要
// 未超过 − 返回原始消息
if compacted != original:
applyToContext(compacted) // 替换 AgentState.context
newInput = ReasoningInput(compacted, systemMsg, options)
return next.apply(newInput) // 用压缩后的输入传递
else:
return next.apply(input) // 原样传递
After 模式:MemoryFlushMiddleware (onAgent)
在整个 call 完成后触发后置操作。
MemoryFlushMiddleware.onAgent(agent, ctx, input, next):
return next.apply(input) // 先让整个 agent 调用完成
.doOnComplete(() => {
doFlush(agent, ctx).subscribe()
// 用 LLM 从对话中提取值得记住的内容
// 写入 memory/YYYY-MM-DD.md
// 将 session 日志归档到 JSONL 文件
})
Pipeline 模式:WorkspaceContextMiddleware (onSystemPrompt)
顺序追加内容到系统提示词,多个中间件依次变换。
WorkspaceContextMiddleware.onSystemPrompt(agent, ctx, prompt):
builder = new StringBuilder(prompt)
builder.append(readAGENTS.md) // 项目指令
builder.append(readMEMORY.md) // 长期记忆
builder.append(readKNOWLEDGE.md) // 知识库
builder.append(currentDate()) // 当前日期
builder.append(osInfo) // 操作系统信息
builder.append(workspacePaths) // 工作区路径
return Mono.just(builder.toString()) // 传给下一个 onSystemPrompt 中间件
Gate 模式:PlanModeMiddleware (onActing)
拦截并拒绝不符合条件的工具调用。
PlanModeMiddleware.onActing(agent, ctx, input, next):
if planModeActive:
filtered = input.toolCalls.filter(tool =>
tool.isReadOnly() OR tool.name in planExtraAllowed
)
for each rejected in (input.toolCalls − filtered):
emit ToolResultBlock("Denied: plan mode restricted")
return next.apply(ActingInput(filtered)) // 只放行只读工具
else:
return next.apply(input) // 非Plan Mode, 全部放行
4. 机制三:工具注册
HarnessAgent.java line 1777, 2007--2048, 2227--2228
执行路径
Builder.build()
├─ agentToolkit = this.toolkit.copy() ← 深拷贝,隔离不同Agent实例
├─ 注册 Harness 专属工具 (在 agentToolkit 上):
│ ├─ MemorySearchTool(wsManager)
│ │ 搜索记忆文件内容,底层走 AbstractFilesystem
│ ├─ MemoryGetTool(wsManager)
│ │ 读取指定记忆文件
│ ├─ SessionSearchTool(wsManager)
│ │ 搜索历史会话日志
│ ├─ FilesystemTool(filesystem, normalizer)
│ │ 统一文件操作入口,多态适配 Local/Sandbox/Remote
│ │ read/write/edit/grep/ls/upload/download/move
│ ├─ ShellExecuteTool(sandbox)
│ │ ← 仅当 filesystem instanceof AbstractSandboxFilesystem
│ │ Shell命令执行,委托给沙箱后端
│ │ 安全边界由注册条件保证
│ ├─ PlanEnterTool / PlanWriteTool / PlanExitTool ← Plan Mode 控制
│ ├─ AgentSpawnTool / TaskTool ← 子Agent编排(来自SubagentsMiddleware)
│ ├─ SkillManageTool / ProposeSkillTool ← 技能自学习
│ └─ NCP Server Tools ← 通过 NcpServerRegistrar 动态注册
├─ inner.toolkit(agentToolkit) ← 最后传给 Core
└─ delegate = inner.build() ← ReActAgent 构造,收集 toolkit
伪代码
// build() 中的工具注册流程
agentToolkit = toolkit.copy() // 深拷贝,避免污染共享模板
// 记忆工具 → 注入 WorkspaceManager
agentToolkit.registerTool(new MemorySearchTool(wsManager))
agentToolkit.registerTool(new MemoryGetTool(wsManager))
agentToolkit.registerTool(new SessionSearchTool(wsManager))
// 文件系统工具 → 注入多态 AbstractFilesystem
agentToolkit.registerTool(new FilesystemTool(filesystem, pathNormalizer))
// FilesystemTool 内部: abstractFilesystem.read(runtimeContext, norm(path), ...)
// 同一个 Tool 代码, filesystem 可能是 Local/Sandbox/Remote → 零改动切换后端
// Shell工具 − 仅沙箱模式注册 (注册条件 = 安全边界)
if filesystem instanceof AbstractSandboxFilesystem sandbox:
agentToolkit.registerTool(new ShellExecuteTool(sandbox))
// ShellExecuteTool 内部: sandbox.execute(runtimeContext, command, timeout)
// Plan Mode 工具
if planModeEnabled:
agentToolkit.registerTool(new PlanEnterTool(planModeManager))
agentToolkit.registerTool(new PlanWriteTool(planModeManager))
agentToolkit.registerTool(new PlanExitTool(planModeManager))
// 子Agent工具(从中间件获取)
if dynamicSubagents:
for tool in dynMw.getTools():
agentToolkit.registerTool(tool)
// 最后传给 Core
inner.toolkit(agentToolkit)
delegate = inner.build()
// ReActAgent 构造时: toolkit.getToolSchemas() 收集所有工具的 schema
// ReActAgent 循环中: toolkit.callTools() 分发执行 → 和普通工具一样被调度
工具与 Harness 基础设施的连接方式
每个 Harness 工具通过构造函数注入 获得基础设施引用,ReActAgent 对它们完全无感知:
┌─────────────────────┐ ┌─────────────────────────────┐
│ FilesystemTool │◄─────────┤ AbstractFilesystem │
│ · read/write/edit/ls│构造注入 │ (多态: Local/Sandbox/Remote) │
└─────────────────────┘ └─────────────────────────────┘
↑
│ 运行时通过 RuntimeContext 传递 userId/sessionId
│ AbstractFilesystem 据此做命名空间隔离
┌─────────────────────┐ ┌─────────────────────────────┐
│ ShellExecuteTool │◄─────────┤ AbstractSandbox │
│ · execute(command) │构造注入 │ Filesystem │
└─────────────────────┘ │ (Local/Docker/K8s) │
└─────────────────────────────┘
↑ execute() 在容器/Pod内执行
本地模式下宿主直接执行
┌─────────────────────┐ ┌─────────────────────────────┐
│ MemorySearchTool │◄─────────┤ WorkspaceManager │
│ · search(query) │构造注入 │ · listMemoryFiles() │
└─────────────────────┘ │ · readUtf8() │
└─────────────────────────────┘
↳ 间接使用Filesystem
涉及的方法
| 方法 | 功能 |
|---|---|
| toolkit.copy() | 深拷贝 Toolkit,确保不同 Agent 实例的工具集互相隔离 |
| agentToolkit.registerTool(tool) | 向 Toolkit 注册一个工具,自动生成 ToolSchema |
| inner.toolkit(agentToolkit) | 将填充好的 Toolkit 设置到 ReActAgent.Builder 上 |
| FilesystemTool(filesystem, normalizer) | 构造时注入多态文件系统和路径规范化器 |
| ShellExecuteTool(sandbox) | 构造时注入沙箱文件系统(仅沙箱模式下创建) |
| MemorySearchTool(wsManager) | 构造时注入 WorkspaceManager,间接使用文件系统 |
| NcpServerRegistrar.register(toolkit, servers) | 动态注册 MCP 服务器暴露的工具 |
5. build() 装配全流程
HarnessAgent.java line 1774--2249
Builder.build()是三种机制的交汇点,按严格顺序装配:
Builder.build()
├─【Step 1】Toolkit 深拷贝 line 1777
│ agentToolkit = this.toolkit.copy()
├─【Step 2】文件系统解析 line 1780--1849
│ filesystem = resolveFilesystem(spec, workspace, nsFactory)
│ 互斥校验: sandbox vs remote vs local vs raw
│ 生成 NamespaceFactory (userid → 命名空间前缀)
├─【Step 3】分布式存储校验 line 1806--1844
│ if distributedStore:
│ 自动注入 stateStore / snapshotSpec / executionGuard
│ if remoteFilesystem + localStateStore:
│ throw IllegalStateException ← 一致性强制
├─【Step 4】WorkspaceManager 创建 line 1885--1887
│ wsManager = new WorkspaceManager(workspace, filesystem, index, nsFactory)
├─【Step 5】沙箱组装(如配置了沙箱) line 1855--1884
│ capturedSandboxFs = new SandboxBackedFilesystem() ← 代理
│ filesystem = capturedSandboxFs
│ sandboxManager = new SandboxManager(client, stateStore, agentId, guard)
│ sandboxLifecycleMw = new SandboxLifecycleMiddleware(manager, proxy)
├─【Step 6】中间件注入 → inner.middleware(...) line 1904--2207
│ 按顺序注入 13 个中间件 (详见第3节执行路径图)
├─【Step 7】工具注册 → agentToolkit.registerTool(...) line 2007--2048
│ 注册所有 Harness 专属工具(详见第4节执行路径图)
├─【Step 8】传给 Core 并构建 line 2227--2228
│ inner.toolkit(agentToolkit)
│ delegate = inner.build()
│ // ReActAgent 构造时:
│ // middlewares = [GracefulShutdownMW] + builder.middlewares
│ // toolkit = agentToolkit (含所有 Harness 工具)
└─【Step 9】构造 HarnessAgent line 2231--2249
new HarnessAgent(delegate, wsManager, sandboxLifecycleMw, ...)
// 保存引用供 wrappedCall() 使用
6. 三种机制的协作关系
以一个完整的 call() 为例,展示三种机制如何在运行时协同:
HarnessAgent.call(msg, ctx)
┌─────────────────────────────────────────────────────────────┐
│ 机制一:外层包装 │
│ ensureSessionDefaults(ctx) 富化 RuntimeContext │
│ wrappedCall(): │
│ acquireForCall(ctx) 启动沙箱容器 │
│ └─ delegate.call(msg, enrichedCtx) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 机制二 + 三: Core 循环 │ │
│ │ onAgent: MemoryFlushMW 注册完成后回调 │ │
│ │ reasoning(0): │ │
│ │ onSystemPrompt: │ │
│ │ 追加 AGENTS.md /MEMORY.md/技能列表 │ │
│ │ onReasoning: CompactionMW 压缩过长上下文 │ │
│ │ onModelCall: TraceMW 记录日志 │ │
│ │ model.stream() LLM 回复包含 ToolUseBlock
│ │ acting(0): │ │
│ │ onActing: PlanModeMW 检查是否允许执行 │ │
│ │ evaluatePermissions() 权限门控 │ │
│ │ toolkit.callTools() │ │
│ │ ├─ FilesystemTool.read() ←机制三: 通过 AbstractFilesystem 读文件
│ │ └─ ShellExecuteTool.exec() ←机制三: 通过 Sandbox 执行命令
│ │ onActing: EvictionMW 超大结果卸载到文件
│ │ reasoning(1) → acting(1) → ... 继续迭代
│ └─ return finalMsg
│ releaseForCall(ctx) 停止沙箱、持久化快照
│ onErrorResume(overflow?) 溢出则压缩重试
└─────────────────────────────────────────────────────────────┘
│ MemoryFlushMW.doOnComplete → doFlush() 异步提取记忆
7. 方法速查表
外层包装(机制一)
| 方法 | 功能 |
|---|---|
| ensureSessionDefaults(ctx) | 富化 RuntimeContext,注入 SandboxContext/FS/WSManager |
| wrappedCall(msg, ctx, inner) | 外层包装入口:沙箱生命周期 + 溢出恢复两层 |
| wrappedStreamEvents(msg, ctx, inner) | 流式版本的外层包装(无溢出恢复) |
| acquireForCall(ctx) | 获取 / 创建沙箱,注入文件系统代理 |
| releaseForCall(ctx) | 持久化状态,停止 / 销毁沙箱,清除代理 |
| isContextOverflowError(e) | 字符串匹配检测上下文溢出错误 |
| recoverFromOverflow(msg, ctx) | 强制压缩后重试整个 call |
中间件(机制二)
| 中间件 | 钩子 | 模式 | 功能 |
|---|---|---|---|
| SandboxLifecycleMiddleware | 无(外部调用) | --- | 沙箱 acquire/release |
| WorkspaceContextMiddleware | onSystemPrompt | Pipeline | 追加工作区上下文到系统提示 |
| CompactionMiddleware | onReasoning | Before | 推理前压缩过长上下文 |
| AtPathExpansionMiddleware | onReasoning | Before | 展开 @path 引用 |
| MemoryFlushMiddleware | onAgent | After | call 后提取记忆 |
| MemoryMaintenanceMiddleware | onAgent | After | 定期归档 / 整合 / 清理 |
| ToolResultEvictionMiddleware | onActing | Around | 超大结果卸载文件 |
| PlanModeMiddleware | onActing | Gate | Plan Mode 拦截非只读工具 |
| AgentTraceMiddleware | onReasoning/onActing/onModelCall | Around | 结构化可观测日志 |
| HarnessSkillMiddleware | onSystemPrompt/onReasoning | Pipeline+Before | 技能解析与注入 |
| SkillUsageMiddleware | onActing | After | 技能调用计数 |
| SkillCuratorMiddleware | onAgent | After | 技能自动晋升 / 降级 |
| SubagentsMiddleware | onSystemPrompt/onActing | Pipeline+After | 子 Agent 编排与任务推送 |
工具注册(机制三)
| 工具 | 注入依赖 | 功能 |
|---|---|---|
| FilesystemTool | AbstractFilesystem + PathNormalizer | 统一文件操作(read/write/edit/grep/ls/upload/download) |
| ShellExecuteTool | AbstractSandboxFilesystem | Shell 命令执行,(仅沙箱模式注册) |
| MemorySearchTool | WorkspaceManager | 关键词搜索记忆文件 |
| MemoryGetTool | WorkspaceManager | 读取指定记忆文件 |
| SessionSearchTool | WorkspaceManager | 搜索历史会话日志 |
| PlanEnterTool/WriteTool/ExitTool | PlanModeManager | Plan Mode 进入 / 写入 / 退出 |
| AgentSpawnTool/TaskTool | SubagentsMiddleware | 子 Agent 生成与任务管理 |
| SkillManageTool/ProposeSkillTool | SkillCurator | 技能自创建 / 编辑 / 提议 |
build() 装配
| 步骤 | 方法 | 功能 |
|---|---|---|
| Step1 | toolkit.copy() | 深拷贝 Toolkit |
| Step2 | resolveFilesystem(...) | 解析文件系统后端(互斥校验) |
| Step3 | 一致性校验 | Remote+LocalState → throw; Sandbox+LocalState → warn |
| Step4 | new WorkspaceManager(...) | 创建工作区管理器 |
| Step5 | new SandboxLifecycleMiddleware(...) | 组装沙箱中间件 + 代理 |
| Step6 | inner.middleware(...) ×13 | 注入中间件到 Core |
| Step7 | agentToolkit.registerTool(...) ×N | 注册 Harness 工具 |
| Step8 | inner.toolkit() + inner.build() | 传给 Core 并构造 ReActAgent |
| Step9 | new HarnessAgent(delegate,...) | 构造 HarnessAgent |
8. 源码索引
| 文件 | 关键内容 |
|---|---|
harness/.../HarnessAgent.java |
Builder.build() 装配全流程 (line 1774--2249)、wrappedCall() (line 731--757) |
harness/.../middleware/SandboxLifecycleMiddleware.java |
acquireForCall/releaseForCall(不走中间件链) |
harness/.../middleware/CompactionMiddleware.java |
onReasoning: Before 模式压缩上下文 |
harness/.../middleware/MemoryFlushMiddleware.java |
onAgent: After 模式提取记忆 |
harness/.../middleware/WorkspaceContextMiddleware.java |
onSystemPrompt: Pipeline 模式追加上下文 |
harness/.../middleware/ToolResultEvictionMiddleware.java |
onActing: Around 模式卸载超大结果 |
harness/.../middleware/PlanModeMiddleware.java |
onActing: Gate 模式拦截非只读工具 |
harness/.../middleware/HarnessSkillMiddleware.java |
onSystemPrompt/onReasoning: 技能解析注入 |
harness/.../middleware/SubagentsMiddleware.java |
onSystemPrompt/onActing: 子 Agent 编排 |
harness/.../tool/FilesystemTool.java |
统一文件操作,注入 AbstractFilesystem |
harness/.../tool/ShellExecuteTool.java |
Shell 执行,注入 AbstractSandboxFilesystem |
harness/.../tool/MemorySearchTool.java |
记忆搜索,注入 WorkspaceManager |
core/.../middleware/MiddlewareBase.java |
中间件基类:5 个钩子方法的默认遗传实现 |
core/.../middleware/MiddlewareChain.java |
洋葱模型链构建器 |
core/.../ReActAgent.java |
5 条链的触发点:line 848/589/1888/2029/21 |