harness - 内置拦截器的修改及添加
solon-ai-harness 在引擎启动时预置了三个内置拦截器,分别负责三种不同的 Agent 运行保障机制:
compressionInterceptor(上下文压缩拦截器):负责工作记忆区的无损/近无损压缩hitlInterceptor(人工介入拦截器):负责敏感工具调用的安全审计与人工审批stopLoopInterceptor(防死循环拦截器):负责检测并纠正 Agent 陷入重复模式
1、内置拦截器的默认行为
HarnessEngine engine = HarnessEngine.of(workspace, harnessHome)
.sessionProvider(sessionProvider)
.build();
默认情况下,引擎会自动创建并配置上述三个拦截器:
上下文压缩拦截器:触发条件为消息超过 40 条或 Token 超过模型上下文窗口的 75%。保留的消息窗口上限为 40 条。使用组合压缩策略(CompositeCompressionStrategy),先后执行:
KeyInfoExtractionStrategy:提取干货(去水),保留关键信息HierarchicalCompressionStrategy:滚动更新摘要,维护分层压缩的历史摘要
防死循环拦截器:默认 (3, 8),即在 8 轮窗口内如果连续 3 次出现完全相同的工具调用特征,则注入自纠错提示。
HITL 拦截器:默认绑定 bash 工具的 BashToolStrategy(内含 6 条内置安全规则 + 权限引擎委托),并配置了 onApproved 审批自动记忆回调。
2、修改现有的内置拦截器
通过 Builder 阶段直接替换拦截器实例:
HarnessEngine engine = HarnessEngine.of(workspace, harnessHome)
.sessionProvider(sessionProvider)
.compressionInterceptor(new ContextCompressionInterceptor()) // 替换压缩拦截器
.hitlInterceptor(new HITLInterceptor()) // 替换 HITL 拦截器
.stopLoopInterceptor(new StopLoopInterceptor(5, 10)) // 替换防死循环拦截器
.build();
注意:Builder 方法
compressionInterceptor()、hitlInterceptor()、stopLoopInterceptor()是替换而非叠加。如需保留默认行为仅调整参数,可通过引擎运行时的 API 调整(见第 5 节)。
3、上下文压缩拦截器(ContextCompressionInterceptor)
拦截器的生命周期方法:重写 onReasonStart 进行压缩判断,并在模型返回上下文超长错误时通过 onReasonRetry 执行强制压缩。
3.1 构造参数说明
// 最简构造(maxMessages=15, maxContextRatio=0.75, 使用默认组合压缩策略)
ContextCompressionInterceptor interceptor = new ContextCompressionInterceptor();
// 指定最大消息数与最简压缩策略
ContextCompressionInterceptor interceptor = new ContextCompressionInterceptor(
40 // maxMessages: 保留窗口最大消息数
);
// 指定完整参数
ContextCompressionInterceptor interceptor = new ContextCompressionInterceptor(
40 // maxMessages: 保留窗口最大消息数(下限 10)
);
interceptor.setMaxContextLengthRatio(0.75); // 模型上下文窗口使用比例(默认 0.75)
interceptor.setMaxRetries(3); // 压缩策略重试次数
interceptor.setMessageTriggerFactor(1.5); // 触发滞后系数(>=1.0,越大越不易触发)
interceptor.setMinReservedMessages(5); // 保留窗口最小消息数(显式覆盖,>=3)
interceptor.setPerMessageCap(8000); // 单条消息 Token 硬上限(0=自动推导)
interceptor.setDefaultContextLength(128_000L); // 模型上下文窗口默认值
3.2 压缩策略(CompressionStrategy)配置
// 自定义组合压缩策略
CompressionStrategy strategy = new CompositeCompressionStrategy()
.addStrategy(new KeyInfoExtractionStrategy())
.addStrategy(new HierarchicalCompressionStrategy());
ContextCompressionInterceptor interceptor = new ContextCompressionInterceptor(
40,
0.75,
3,
strategy);
HarnessEngine engine = HarnessEngine.of(workspace, harnessHome)
.sessionProvider(sessionProvider)
.compressionInterceptor(interceptor)
.build();
也可通过引擎运行时动态调整压缩参数:
engine.setCompressionThreshold(50, 0.80); // 调整阈值(maxMessages=50, ratio=0.80)
3.3 压缩保护机制
压缩拦截器内置了多层保护机制:
- 初心链保护:标记为
META_FIRST的消息(system prompt、用户原始问题)永不压缩 - Tool-use 原子对保护:
Assistant(with tool_calls)↔ToolMessage的调用-结果配对不会被拆散 - 语义连贯补齐:截断点前的 Assistant thought 消息会一并纳入保留区
- 单条消息守卫:单条超过
perMessageCap的消息会被头尾截断,防止巨量消息撑爆上下文 - 压缩效果告警:压缩后消息数 >= 原始 90% 时输出 WARN 日志
4、HITL 拦截器(人工介入)
HITL(Human-in-the-Loop)拦截器通过 ReAct 协议的生命周期钩子实现流程管控。
4.1 生命周期机制
| 方法 | 说明 |
|---|---|
onAgentStart | 恢复挂起会话时自动判断是否回到 Action |
onReasonStart | 推理开始前检查,无操作 |
onActionStart | 批级预检:扫全量 tool call,未决策的整批挂起 |
onToolCallStart | 单工具级防已决策二次应用 |
onToolCallEnd | 清理决策键、注入人工备注 |
onActionEnd | 会话未挂起时清理批挂起状态 |
4.2 使用方式
开关控制:HITLInterceptor 本身和 hitlEnabled 开关需同时满足才能生效:
// Builder 阶段配置
HarnessEngine engine = HarnessEngine.of(workspace, harnessHome)
.sessionProvider(sessionProvider)
.hitlEnabled(true) // 全局启用 HITL 开关
.build();
hitlEnabled=true为全局开关,拦截器仅为具体策略逻辑,两者缺一不可。
4.3 注册工具策略
通过 Builder 指定自定义 HITL 拦截器(此时会覆盖默认的 BashToolStrategy):
HITLInterceptor hitl = new HITLInterceptor();
// 使用内置策略
hitl.onTool("bash", new BashToolStrategy()); // bash 命令审计
hitl.onTool("write", new WriteToolStrategy("write")); // 文件写入审计
hitl.onTool("edit", new WriteToolStrategy("edit")); // 文件编辑审计
hitl.onTool("webfetch", new WebToolStrategy("webfetch")); // 网络访问审计
// 快速注册为敏感工具(使用默认敏感策略)
hitl.onSensitiveTool("write", "edit", "rm");
// 设置审批自动记忆回调(审批过的相同命令/路径自动放行)
hitl.onApproved((toolName, args) -> {
engine.addPermissionRule(PermissionRule.allow(toolName));
});
HarnessEngine engine = HarnessEngine.of(workspace, harnessHome)
.sessionProvider(sessionProvider)
.hitlInterceptor(hitl)
.hitlEnabled(true)
.build();
4.4 运行时动态开关
// 运行时启用/禁用 HITL
engine.setHitlEnabled(true); // 启用,会调用 hitlInterceptor.setEnabled(true)
engine.setHitlEnabled(false); // 禁用
engine.setHitlEnabled()会同时更新HarnessOptions.hitlEnabled和HITLInterceptor.setEnabled(),保持双开关一致。
4.5 内置 HITL 策略
| 策略类 | 适用工具 | 内置规则 |
|---|---|---|
BashToolStrategy | bash | 空命令放行、注入防御、系统特权黑名单、路径回溯防御、敏感文件防御、只读命令自动放行、不完整命令拒绝 |
WriteToolStrategy | write/edit | 路径回溯防御、敏感系统文件防御 |
WebToolStrategy | webfetch/websearch | 高风险域名黑名单 |
HITLSensitiveStrategy | 任意 | 通用敏感操作拦截 |
详细参考:《harness - HitlStrategy 安全审计策略》 和 《harness - PermissionRule 权限规则引擎》。
4.6 流式事件
HITL 拦截器在流式输出时会推送专门的事件:
HITLPendingEvent— 首次拦截挂起,携带HITLTask列表(callId、toolName、args、comment),用于前端展示审批卡片HITLDecidedEvent— 决策生效,携带HITLDecision(approved/skipped/rejected、modifiedArgs、comment),用于关闭/更新审批卡片
5、防死循环拦截器(StopLoopInterceptor)
通过 onThought 生命周期方法检测 Agent 的重复行为模式。使用规范化指纹(归一化的工具调用名组合/Action 行/内容前缀)标识每次迭代意图。
// 自定义参数:5 次相同特征 / 10 轮窗口
StopLoopInterceptor stopLoop = new StopLoopInterceptor(5, 10);
HarnessEngine engine = HarnessEngine.of(workspace, harnessHome)
.sessionProvider(sessionProvider)
.stopLoopInterceptor(stopLoop)
.build();
检测到循环后的处理机制:
- 生成自纠错提示文案(含有效性评估、强制收敛、避免重复三条指导)
- 作为用户消息注入到工作记忆区(
ChatMessage.ofUser()) - 清除计数器,避免注入消息被重复计数
- 不挂起会话、不强制终止,LLM 在下一轮推理时看到提示并自行纠正
6、添加自定义拦截器(通过 HarnessExtension)
通过 HarnessExtension 接口,可以在 Builder 构建阶段或引擎运行阶段对 Agent 的行为进行扩展。
6.1 HarnessExtension 接口定义
public interface HarnessExtension {
/** 在 Agent 构建时回调,参数为当前构建的 agentName 与 agentBuilder */
void configure(String agentName, ReActAgent.Builder agentBuilder);
}
6.2 Builder 构建阶段
HarnessEngine engine = HarnessEngine.of(workspace, harnessHome)
.sessionProvider(sessionProvider)
.extensionAdd(new HarnessExtension() {
@Override
public void configure(String agentName, ReActAgent.Builder agentBuilder) {
// 添加自定义拦截器
agentBuilder.defaultInterceptorAdd(new ReActInterceptor() {
@Override
public void onAgentStart(ReActTrace trace) {
// 自定义逻辑
System.out.println("Agent started: " + agentName);
}
});
// 可以设置拦截器优先级(数字越小优先级越高)
// agentBuilder.defaultInterceptorAdd(5, interceptor);
}
})
.build();
注意:
defaultInterceptorAdd与compressionInterceptor()、hitlInterceptor()等替换式方法不同,它是添加而非替换。替换式方法会替换掉引擎默认创建的拦截器实例。
6.3 运行时动态添加/移除
引擎运行后,通过以下 API 动态管理扩展(每次变更会触发主 Agent 重建):
// 运行时添加扩展
engine.addExtension(new HarnessExtension() {
@Override
public void configure(String agentName, ReActAgent.Builder agentBuilder) {
agentBuilder.defaultInterceptorAdd(new MyCustomInterceptor());
}
});
// 运行时移除扩展
engine.removeExtension(extension);
6.4 自定义拦截器注册到具体生命周期的方法
ReActAgent.Builder 提供了以下注册方法:
| Builder 方法 | 说明 |
|---|---|
defaultInterceptorAdd(ReActInterceptor) | 添加默认拦截器到所有生命周期 |
defaultInterceptorAdd(int priority, ReActInterceptor) | 指定优先级的拦截器(数值越小越靠前执行) |
defaultTalentAdd(Talent) | 添加 Talent(技能包),包含多个工具 |
关于完整的 ReActInterceptor 生命周期方法,参见 《react - ReActInterceptor 拦截器》。
7、通过 AgentDefinition 的 tools 配置启用 HITL
在 Agent 定义的工具体系中,hitl 是一个特殊的工具名,通过它来启用人工介入拦截器:
AgentDefinition definition = AgentDefinition.builder()
.name("my-agent")
.toolsAdd("read", "write", "bash", "hitl") // 启用 HITL 拦截器
.build();
当 hitl 出现在工具列表中,且 hitlEnabled=true 时,Agent 会自动将 HITLInterceptor 添加到 ReAct 拦截器链中。
8、完整示例:组合使用三种拦截器
// 1. 自定义压缩策略
CompressionStrategy strategy = new CompositeCompressionStrategy()
.addStrategy(new KeyInfoExtractionStrategy())
.addStrategy(new HierarchicalCompressionStrategy());
ContextCompressionInterceptor compressionInterceptor =
new ContextCompressionInterceptor(40, 0.75, 3, strategy);
// 2. 自定义 HITL 策略
HITLInterceptor hitl = new HITLInterceptor()
.onTool("bash", new BashToolStrategy())
.onTool("write", new WriteToolStrategy("write"))
.onSensitiveTool("rm", "del")
.onApproved((toolName, args) -> {
engine.addPermissionRule(PermissionRule.allow(toolName));
});
// 3. 构建引擎
HarnessEngine engine = HarnessEngine.of(workspace, harnessHome)
.sessionProvider(sessionProvider)
.compressionInterceptor(compressionInterceptor)
.hitlInterceptor(hitl)
.hitlEnabled(true)
.stopLoopInterceptor(new StopLoopInterceptor(5, 12))
.toolsAdd("read", "write", "edit", "bash", "glob", "grep", "ls", "hitl")
.build();
// 运行时动态调整
engine.setHitlEnabled(true);
engine.setCompressionThreshold(50, 0.80);