Agent Harness 实战指南:构建生产级 AI Agent 的"马具"框架

你有没有想过,一个 AI Agent 除了"会思考",还得"能干活"才能落地?模型负责推理,但谁来负责工具调用、会话管理、上下文压缩、安全审计和沙盒隔离?Solon AI 4.0 的 solon-ai-harness 就是干这个的------它不是一个 ChatModel 封装,而是一个专门为 AI Agent 打造的生产级执行框架。

官方定义说得好:Agent = Model + Harness。模型提供推理,Harness 提供落地执行的实体。如果说模型是 CPU,上下文是内存,那 Harness 就是操作系统------负责资源调度与环境隔离。

solon-ai-harness 已经在 SolonCode CLI 项目中经过了多个月的实战验证,本文带你从零上手。

1. 添加依赖

xml 复制代码
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-ai-harness</artifactId>
    <version>${solon.version}</version>
</dependency>

solon-ai-harness 同时支持 Java 8 到 Java 25,兼容性不用担心。

2. 快速上手:Hello World

先来一个最简单的例子:构建一个只拥有四个基础工具(读、写、编辑、执行命令)的 Agent,让它帮我们完成一个任务。

java 复制代码
import org.noear.solon.ai.agent.AgentSession;
import org.noear.solon.ai.agent.session.InMemoryAgentSession;
import org.noear.solon.ai.agent.session.AgentSessionProvider;
import org.noear.solon.ai.chat.ChatConfig;
import org.noear.solon.ai.harness.HarnessEngine;
import org.noear.solon.ai.harness.permission.ToolPermission;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DemoApp {
    public static void main(String[] arg) throws Throwable {
        // 会话提供者:决定会话如何存储(这里用内存实现)
        AgentSessionProvider sessionProvider = new AgentSessionProvider() {
            private final Map<String, AgentSession> sessionMap = new ConcurrentHashMap<>();

            @Override
            public AgentSession getSession(String instanceId) {
                return sessionMap.computeIfAbsent(
                    instanceId, k -> InMemoryAgentSession.of(k)
                );
            }
        };

        // 构建 Harness 引擎
        HarnessEngine engine = HarnessEngine.of("/data/work/", ".tmp")
                .systemPrompt("你是一个 AI 助手,请根据用户指令完成任务。")
                .sessionProvider(sessionProvider)
                .toolsAdd(ToolPermission.TOOL_PI)  // 仅开放 read/write/edit/bash
                .modelAdd(new ChatConfig().then(slf -> {
                    slf.setApiUrl("https://api.deepseek.com");
                    slf.setApiKey("sk-你的Key");
                    slf.setModel("deepseek-chat");
                }))
                .build();

        // 执行任务
        engine.prompt("在当前目录下创建一个 README.md,内容为 '# Hello Harness'")
                .call();
    }
}

就这么简单。HarnessEngine.of(workspace, harnessHome) 的两个参数:

  • workspace:Agent 的当前工作目录(文件读写、命令执行都以它为根)
  • harnessHome:马具主目录(会话、记忆、技能等运行态数据落在这里)

3. 核心配置详解

HarnessEngine.Builder 提供了一整套配置项,让你精细控制 Agent 的行为。

3.1 基础配置

java 复制代码
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .systemPrompt("你是一个 AI 助手")          // 系统提示词
        .sessionWindowSize(8)                       // 会话历史窗口大小(默认 8)
        .compressionThreshold(30, 30_000)           // 触发压缩:30 条消息或 30000 token
        .maxTurns(20)                               // 最大循环步数(默认 20)
        .autoRethink(true)                          // 最大步数自动续航(默认 true)
        .modelRetries(3)                            // 模型重试次数(默认 3)
        .build();

3.2 工具权限配置

toolsAdddisallowedToolsAdd 控制 Agent 可以调用哪些工具:

java 复制代码
// 开放所有工具(包括 bash、文件读写、网络搜索等)
.toolsAdd(ToolPermission.TOOL_ALL_FULL)

// 或仅开放基础工具
.toolsAdd(ToolPermission.TOOL_PI)    // read, write, edit, bash

// 或精细控制:开放,再禁用某些
.toolsAdd(ToolPermission.TOOL_ALL_FULL)
.disallowedToolsAdd(ToolPermission.TOOL_BASH)  // 禁止 bash 执行

工具权限分为公域和私域:

  • 公域:read、write、edit、bash、websearch、webfetch、glob、grep、ls、skill、task 等
  • 私域:generate(生成子代理)、高阶权限需人工确认

3.3 沙盒安全配置

java 复制代码
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .sandboxEnabled(true)          // 启用沙盒模式
        .sandboxAllowUserHome(true)    // 允许访问用户主目录
        .sandboxSystemRestrict(true)   // 系统级限制,危险操作需人工确认
        .build();

沙盒模式下,Agent 只能访问工作区和指定的挂载目录,不能越界到系统目录。

3.4 多模型配置

java 复制代码
.modelAdd(new ChatConfig().then(slf -> {
    slf.setApiUrl("https://api.deepseek.com");
    slf.setApiKey("sk-***");
    slf.setModel("deepseek-chat");
}))
.modelAdd(new ChatConfig().then(slf -> {
    slf.setApiUrl("https://api.openai.com/v1");
    slf.setApiKey("sk-***");
    slf.setModel("gpt-4o");
}))

第一个添加的模型是主模型,运行时可以通过 options() 动态切换。

4. 调用与流式响应

engine.prompt(...) 返回的是一个 ReActRequest 接口,支持同步和流式两种模式。

4.1 同步调用

java 复制代码
engine.prompt("帮我查一下当前目录有哪些 Java 文件").call();

4.2 流式响应

适合需要实时展示 Agent 推理过程或逐步输出结果的场景:

java 复制代码
engine.prompt("分析当前项目的代码结构并生成文档").stream();

4.3 指定会话与请求选项

java 复制代码
// 获取或创建持久会话
AgentSession session = engine.getSession("default");

engine.prompt("帮我重构 UserService 类的逻辑")
        .session(session)   // 绑定持久会话(不指定则为临时会话)
        .options(o -> {
            // 切换大模型
            o.chatModel(engine.getModelOrMain("gpt-4o"));

            // 动态指定工作区
            o.toolContextPut(HarnessEngine.ATTR_CWD, "/data/projects/myapp");
        })
        .call();

5. 子代理与任务委派

solon-ai-harness 内置了几个开箱即用的子代理:

名称 说明
general 通用全能专家。其他子代理不匹配时用它
explore 全域信息探索专家(本地文件 + 全网检索,无写权限)
plan 规划与计划专家(制定逻辑路径与执行步骤)
bash Bash 命令执行专家(git、命令行操作)

5.1 通过代码动态创建子代理

java 复制代码
AgentSession session = engine.getSession("default");

AgentDefinition definition = new AgentDefinition();
definition.setSystemPrompt("你是一个 Git 专家,负责执行仓库操作。");
definition.getMetadata().addTools(ToolPermission.TOOL_BASH);

ReActAgent gitAgent = engine.createSubagent(definition).build();
gitAgent.prompt("提交当前所有更改并推送")
        .session(session)
        .call();

5.2 通过 AgentManager 管理

java 复制代码
// 获取内置子代理
AgentDefinition bashDef = engine.getAgentManager().getAgent("bash");

// 构建并执行
ReActAgent bashAgent = bashDef.builder(engine).build();
bashAgent.prompt("列出当前目录所有 Java 文件").call();

// 获取所有可用子代理
Collection<AgentDefinition> agents = engine.getAgentManager().getAgents();

// 动态注册自定义子代理
engine.getAgentManager().addAgent(customDefinition);

5.3 任务委派(task / multitask)

主代理拥有 task 工具权限时,可自主把子任务委派给子代理:

  • task(agentName, prompt):委派单一任务(串行)
  • multitask([task1, task2, ...]):并行执行多个独立子任务

子任务上下文隔离,委派时必须在 prompt 中提供所有背景信息。

6. 会话与心智记忆

会话保存单次对话的上下文,心智记忆则在多次对话之间长期留存关键事实。

6.1 会话提供者

sessionProvider 是构建引擎的必填项

java 复制代码
// 自定义持久化(如文件/数据库)
.sessionProvider(sessionId -> {
    // 从数据库加载或创建会话
    return MyDatabase.loadSession(sessionId);
})

会话的运行态数据默认落在 {harnessHome}/sessions/ 下。

6.2 心智记忆(Memory)

心智记忆让 Agent 记住用户的偏好、项目规约等关键事实,并在需要时检索召回。

java 复制代码
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .memoryEnabled(true)   // 默认开启
        .memoryProvider(new MemorySolutionProvider() {
            @Override
            public MemorySolution getSolution(String workspace) {
                // 每个工作区可以有不同的记忆方案
                return new MarkdownMemorySolution(workspace);
            }
        })
        .build();

记忆方案内置 Markdown 实现(零外部依赖),也支持 Redis、Lucene、向量库等。Agent 可在运行中自主进行:

  • 提取:写入事实
  • 召回:按 key 精确取
  • 语义检索:自然语言检索
  • 认知整合:碎片升维合并
  • 修剪:删除过时认知

7. 内置拦截器

Harness 有三个内置拦截器,负责保障 Agent 的稳定运行。

7.1 上下文压缩拦截器

防止上下文超出 Token 限制:

java 复制代码
import org.noear.solon.ai.harness.interceptor.*;
import org.noear.solon.ai.harness.compression.*;

CompressionStrategy strategy = new CompositeCompressionStrategy()
        .addStrategy(new KeyInfoExtractionStrategy())     // 提取干货,去水
        .addStrategy(new HierarchicalCompressionStrategy()); // 滚动更新摘要

ContextCompressionInterceptor interceptor = new ContextCompressionInterceptor(
        40,       // 消息条数阈值
        60_000,   // 内容长度阈值
        3,        // 重试次数
        () -> engine.getModelOrMain(engine.getCompressionModel()),
        strategy);

HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .compressionInterceptor(interceptor)
        .build();

7.2 停止循环拦截器

防止 Agent 陷入死循环:

java 复制代码
StopLoopInterceptor stopLoop = new StopLoopInterceptor(5, 10);
// 5 次相同错误 / 10 轮窗口内触发停止

HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .stopLoopInterceptor(stopLoop)
        .build();

7.3 HITL 人工介入拦截器

对高危操作进行人工确认:

java 复制代码
engine.setHitlEnabled(true);

HITLInterceptor hitl = new HITLInterceptor();
hitl.onTool("bash", new HitlStrategy());    // bash 命令需要人工确认
hitl.onTool("write", new WriteAuditStrategy());  // 写入操作审计

HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .hitlInterceptor(hitl)
        .hitlEnabled(true)
        .build();

7.4 自定义拦截器

java 复制代码
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .extensionAdd((agentName, agentBuilder) -> {
            agentBuilder.defaultInterceptorAdd(new ReActInterceptor() {
                @Override
                public void onAgentStart(ReActTrace trace) {
                    System.out.println("Agent " + agentName + " 开始执行,traceId=" + trace.getTraceId());
                }
            });
        })
        .build();

8. 扩展定制

8.1 注册 OpenAPI 接口

让 Agent 调用外部业务系统的 REST API:

java 复制代码
engine.addApiServer(new ApiSource().then(s -> {
    s.setDocUrl("http://xx.xx.xx/v3/api-docs");  // Swagger/OpenAPI 文档地址
    s.setApiBaseUrl("http://xx.xx.xx/");
}));

8.2 注册自定义工具

java 复制代码
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .extensionAdd((agentName, agentBuilder) -> {
            // 注册自定义业务工具
            agentBuilder.defaultToolAdd(new OrderQueryTool());
            agentBuilder.defaultToolAdd(new InventoryCheckTool());
        })
        .build();

8.3 动态系统提示词

java 复制代码
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .extensionAdd((agentName, agentBuilder) -> {
            if ("main".equals(agentName)) {
                agentBuilder.systemPrompt(context -> {
                    String project = context.get("projectName");
                    return "你是 " + project + " 项目的 AI 助手,请严格遵循项目规范。";
                });
            }
        })
        .build();

// 也可以在运行时动态修改
engine.setSystemPrompt("新的系统提示词");

8.4 Prompt Caching

对于支持缓存的大模型(如 Anthropic Claude),可以减少重复计算:

java 复制代码
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .cacheControl(CacheControl.of(
            CacheControl.Type.EPHEMERAL,  // 临时缓存
            "user", 60_000                // 用户的最后一条消息缓存 60 秒
        ))
        .build();

9. 目录结构与挂载机制

马具主目录(harnessHome)按用途划分子目录:

子目录 用途
sessions/ 会话与任务清单持久化
skills/ 技能(SKILL.md
agents/ 子代理定义(Markdown)
commands/ 自定义命令
memory/ 心智记忆
download/ 下载缓存

除了内置目录,还可以通过"挂载"把外部目录接入马具:

java 复制代码
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
        .mountAdd(MountDir.builder()
                .alias("@team-agents")          // 别名,须以 @ 开头
                .type(MountType.AGENTS)         // 挂载类型
                .path("~/team/agents/")         // 物理路径
                .primary(true)                  // 原始挂载,不可删除
                .writeable(false)               // 只读
                .build())
        .mountAdd(MountDir.builder()
                .alias("@global-skills")
                .type(MountType.SKILLS)
                .path("~/.soloncode/skills/")
                .build())
        .build();

挂载后可通过别名直接引用:@team-agents/code-reviewer.md,Harness 自动转换为真实路径。

运行时动态管理:

java 复制代码
engine.addMount(mountDir);          // 添加挂载
engine.hasMount("@team-agents");    // 是否存在
engine.refreshMount("@team-agents"); // 重新扫描
engine.removeMount("@team-agents");  // 移除(primary 不可移除)

engine.getMounts();                 // 所有挂载
engine.getAgents();                 // 所有子代理
engine.getSkills();                 // 所有技能

10. 完整实战:代码审查 Agent

下面是一个完整的例子,结合了本节介绍的大部分特性:

java 复制代码
import org.noear.solon.ai.agent.AgentSession;
import org.noear.solon.ai.agent.session.InMemoryAgentSession;
import org.noear.solon.ai.agent.session.AgentSessionProvider;
import org.noear.solon.ai.chat.ChatConfig;
import org.noear.solon.ai.harness.HarnessEngine;
import org.noear.solon.ai.harness.permission.ToolPermission;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CodeReviewApp {
    public static void main(String[] arg) throws Throwable {
        AgentSessionProvider sessionProvider = new AgentSessionProvider() {
            private final Map<String, AgentSession> sessionMap = new ConcurrentHashMap<>();
            @Override
            public AgentSession getSession(String instanceId) {
                return sessionMap.computeIfAbsent(
                    instanceId, k -> InMemoryAgentSession.of(k)
                );
            }
        };

        HarnessEngine engine = HarnessEngine.of("/data/projects/myapp", ".soloncode/")
                .systemPrompt("你是一个资深代码审查专家,负责分析代码质量。")
                .sessionProvider(sessionProvider)
                .sessionWindowSize(12)
                .compressionThreshold(50, 40_000)
                .maxTurns(30)
                .autoRethink(true)
                .toolsAdd(ToolPermission.TOOL_PI)   // read, write, edit, bash
                .modelAdd(new ChatConfig().then(slf -> {
                    slf.setApiUrl("https://api.deepseek.com");
                    slf.setApiKey("sk-***");
                    slf.setModel("deepseek-chat");
                }))
                .sandboxEnabled(true)
                .build();

        // 第一次对话:审查代码
        AgentSession session = engine.getSession("review-001");
        engine.prompt("审查 src/main/java/com/example/UserService.java,"
                     + "检查潜在问题并输出审查报告到 code-review.md")
                .session(session)
                .call();

        // 第二次对话:基于历史,修复问题
        engine.prompt("根据刚才生成的审查报告,修复代码中的问题")
                .session(session)
                .call();
    }
}

总结

solon-ai-harness 提供了一套从"能思考"到"能干活"的完整基础设施。它的核心能力可以概括为四个维度:

  1. 组织工具使用 --- 权限控制、MCP 协议、输入输出标准化
  2. 管理记忆与会话 --- 持久化、上下文压缩、心智记忆
  3. 环境隔离与安全 --- 沙盒模式、HITL 人工确认、循环检测
  4. 生态扩展 --- OpenAPI 接入、自定义工具、挂载机制

如果你正在构建一个需要稳定、可控、可扩展的 AI Agent 应用,Harness 是值得认真看看的方案。


参考文档:

相关推荐
带刺的坐椅1 小时前
Agent Harness 实战指南:构建生产级 AI Agent 的"马具"框架
java·ai·llm·agent·solon-ai
c238562 小时前
互斥锁高频面试题全解:从基础概念到底层实现,一文通关
java·c++·面试·职场和发展
error:(2 小时前
【系统与实战双精通】VS Code 调试 ROS2 Python 节点与 Launch 系统指南
android·java·python
Tian_Hang3 小时前
Eclipse Ditto 的权限策略
java·服务器·前端·网络·ide·ubuntu·eclipse
小蓝波3 小时前
docker 的命令
java·docker·容器
AI人工智能+电脑小能手3 小时前
【大白话说Java面试题 第155题】【06_Spring篇】第15题:Spring 如何解决循环依赖问题?
java·spring·循环依赖·三级缓存·aop代理
唐青枫4 小时前
Java Maven 实战指南:从 POM、依赖管理到多模块构建
java
硕风和炜4 小时前
【LeetCode: 1301. 最大得分的路径数目 + DP】
java·算法·leetcode·动态规划·dp·记忆化搜索
HjhIron4 小时前
用RAG + Node.js 手搓一个语义搜索引擎,真香!
aigc·ai编程