摘要:一个什么都没配置的 HarnessAgent,默认挂了 13 个中间件、23 个工具,还把账本落到了家目录。本文从一个解剖端点出发,拆这份清单:哪些是固定的,哪些随配置变;12 项能力为什么不是 12 个中间件;默认值翻转之后,状态到底住在哪。最后落到中间件模型的边界:横切归中间件,控制流归引擎——别家也有整机,差别在默认装多厚、装在哪个栈上。
共约 7328 字 · 阅读约 18 分钟
1、开场:一个什么都没配的 HarnessAgent,替我装了 13 个中间件
第 9 课我先加了个只读端点:GET /workbench/harness/anatomy。它只装配、不调用——模型换成 fake 占位,业务工具一个没注册。以下数字都是 AgentScope 2.0.3 的。
HarnessAgent.builder()
.name("anatomy-probe")
.model(new FakeChatModel())
.workspace(Path.of("data/harness-probe/workspace"))
.middleware(new ProbeUserMiddleware()) // order=50,扮演“你自己写的那个”
.build();
FakeChatModel 是我项目里的假模型;ProbeUserMiddleware 只有一行 implements MiddlewareBase,order() 返回 50。
端点回来的三个数:
middlewares 13 (core 1 + user 1 + harness 11)
tools 23
stateStore JsonFileAgentStateStore(非 null)
响应本体是 JSON:baseline.middlewares[]、baseline.tools[]、baseline.stateStore,以及 variants[] 三组差集;上面三行是我摘出来的。
开这个端点之前,我先押了四注:中间件 1 个、排第 0 位、工具 20 上下、stateStore 为 null。只中一注——工具数是 23。
- 我对 Harness 的想象本来是空壳:能力按需打开,不配置就什么都没有。方向反了,默认全含,你要做的是关。
- 还有半个误解是顺手带出来的:我以为这 13 个就是官方说的“十二项能力”的实体。这个等式也不成立,§5.2 给判据。剩下三个问题:13 个谁装的、哪些钉死、哪些随配置变。
想自己看一遍:./mvnw spring-boot:run,然后 curl 127.0.0.1:8080/workbench/harness/anatomy。没有这份工程,getDelegate().getClass() 和 getMiddlewares().size() 两行也能验前两个数。探针把 agentscope.state.home 指到 data/harness-probe/state,build 完 close()——不指的话,它会往你家目录写账本。服务没起,lab.html 会打“解剖失败:(服务在跑吗?)”;端点 404 先查端口和 path-prefix。
2、先把边界划清:Harness 不是引擎
2.1 Harness 是模型之外的工程系统
官方的定义是一句长句,我抄下来:
Harness = 模型之外、围绕 Agent Loop 组织上下文、能力、状态、环境与控制机制,并把模型判断转化为可执行、可恢复、可验证任务过程的代码 / 配置 / 执行逻辑。
“模型之外”四个字是重点。这套东西一行都不进模型权重,全是 Java 代码和目录里的文件。
它也不是更长的提示词。你把公司单证规则写满五千字塞进 AGENTS.md,上下文该爆还是爆;真让上下文不溢出的,是 CompactionMiddleware 在事件流上截断和摘要,那是代码在动。
读图三步:谁包谁——中间件链把 ReAct 循环整个包住;谁连谁——循环向外调模型,向下经 Toolkit 执行工具;状态住哪——账本虚线回存 store,人格与文件在 workspace。后面几节都在拆这张图的零件。
2.2 三个容易混的边界
- 它是多轮推理之外的确定性逻辑。 提示词只是某一轮生成的输入之一;计划、权限检查、错误处理、完成验证,都不在那段字里。
- 它是“Agent 怎么工作”这一层。 Coding Agent 的 CLI 自带一套,托管平台把 Harness 连着执行服务一起卖;它描述的是系统层,不是某个产品形态。
- 它和 Runtime、Sandbox 分工。 Harness 决定下一步给模型看什么、允许它提什么;Runtime 管进程、队列、调度;Sandbox 管文件、网络、进程能碰到哪。
这三条边界都指向同一个地方:你半夜被叫起来的时候,想去改的那一行代码在哪一层。
2.3 五层责任边界:出问题先定责
官方把责任切成五层。我抄成表,每层配一个我遇到或见过的症状:
这张表我拿来定责,不拿来背。上下文爆了找压缩,Worker 挂了看队列,越权写文件看沙箱。三条修法落在三个地方,换模型一条都不解决。
定责比记定义有用。
3、三条原理
三条原理是一根链:能力挂在循环上,能力之间只靠三个对象说话,装配顺序由框架定。
3.1 能力叠加,不改循环
两行就能看出外壳里是什么:
agent.getDelegate().getClass() // io.agentscope.core.ReActAgent
agent.getDelegate().getMiddlewares().size() // 13
getDelegate() 拿出来的是里面那台引擎,跟 Spring 的代理对象一个道理:你在外壳上叫的每个方法,最后都落到被包住的那台机器上。HarnessAgent 没有自己的循环。
13 个中间件挂在 ReAct 循环的五个钩子上。第 7 课拆过钩子,这里只要结论:循环是商品,能力才是差异。
为什么是商品?ReAct 循环在哪个框架里都长差不多——一个 while,一次模型调用,一轮工具执行。真正分出高下的是上下文怎么压、状态怎么存、危险动作怎么拦,这三件事全是横切,天然适合挂中间件。循环既然是商品,官方列的十二项就是它的能力清单:
这张表最大的用处不是背。它让我看清:十二项里有几项,在默认装配里连中间件都摸不到——状态持久化在 core,Channel 在网关。压缩和卸载虽然挂在链上,也只是第 8、9 位的两行名字,不看文档根本对不上号。
注意:这张表按“能力”切;§8.1 会换一套切法(按“有没有内建”),两张表不要混着读。
3.2 三个共享对象
能力之间不互相调用。能插拔的前提就在这里:记忆不该知道压缩在,压缩不该知道子代理在。它们只认三个对象,各管一段:
举个实物:0006 的审批规则能跨请求活下来,我起初以为是某个中间件记着。不是。规则在 AgentState.permissionContext 里,随账本落盘,下一次调用跟着账本一起回来,中间件只是读它。
三个对象就是三个插座,谁想用电谁自己插;插头形状是这三个,不是中间件之间的私有接口。
3.3 装配顺序:固定的是顺序,不是成员
官方原话:
内置 middleware 注册顺序固定,你自己加的会跑在最前面。
这句话我第一遍直接信了,第二遍发现自己把“最前面”理解错了。完整清单在 §4.1 一次看全,这里只记结论:我那个 order=50 的排在第 1——跑在 Harness 十一个之前,但排在 core 的 GracefulShutdown 之后。所以“最前面”的准确口径是:内置之前、Shutdown 之后。
order 只在用户中间件之间排序。十一个内置全是 order=1,谁先谁后不靠这个数——按推断,那是框架写死的注册顺序(一次快照分不清排序与注册序,我只确定用户 order 在用户之间生效)。你插进来的那些,才按 order 降序排,排完整体塞在内置前面。
索引 0 是最外层,链从清单尾往头套——第 7 课那套 MiddlewareChain 的老规矩。这里两个坑留到下一节填:索引 0 那位为什么钉死?2 到 12 这十一行是不是常量?第二个问题的答案是否,而且否定得比我想的彻底。
4、13 个中间件从哪来
4.1 默认清单与五组归类
端点打出来的清单,我盯着看了一会儿。13 行里 12 行的 order 都是 1,只有我塞的那行是 50——order 在这条链里几乎没干活。
0 order=1 core GracefulShutdownMiddleware
1 order=50 user ProbeUserMiddleware
2 order=1 harness AgentTraceMiddleware
3 order=1 harness WorkspaceContextMiddleware
4 order=1 harness AtPathExpansionMiddleware
5 order=1 harness TranscriptMiddleware
6 order=1 harness MemoryFlushMiddleware
7 order=1 harness MemoryMaintenanceMiddleware
8 order=1 harness CompactionMiddleware
9 order=1 harness ToolResultEvictionMiddleware
10 order=1 harness InboxMiddleware
11 order=1 harness DynamicSubagentsMiddleware
12 order=1 harness HarnessSkillMiddleware
读图:0 是 core,1 是你,2 到 12 是 Harness 内置。
这十一行我按名字归了五个组:
- 留痕一个:AgentTrace
- 人格与路径三个:WorkspaceContext、AtPathExpansion、Transcript
- 记忆与上下文四个:MemoryFlush、MemoryMaintenance、Compaction、ToolResultEviction
- 协作两个:Inbox、DynamicSubagents
- 技能一个:HarnessSkill
读完这十一个名字,有件事通了:默认装配里没有计划模式、没有沙箱、没有团队。它们等你开口才进来。
还有一点是我读第二遍才看出来的:索引 6 和 7 挨在一起,一个刷记忆、一个做维护;索引 8 和 9 也挨着,一个压上下文、一个把大结果挪到盘上。挨着放的往往是同一件事的两步,所以清单的邻居关系可以当阅读顺序用——你不用先看文档,看这 13 行就能猜到谁跟谁在配合。
能不能关:我只验过两个 disable(压缩、子代理)。其余几个有没有对应开关,我没一个个试,别拿我的话当清单用。
4.2 GracefulShutdown 为什么永远最外
javap 打 ReActAgent 的私有构造器 ReActAgent(Builder, Toolkit),就四行语句。命令是 javap -p -c -classpath agentscope-core-2.0.3.jar io.agentscope.core.ReActAgent;不带 -c 只看得到签名:
List<MiddlewareBase> list = new ArrayList<>();
list.add(new GracefulShutdownMiddleware(GracefulShutdownManager.INSTANCE));
list.addAll(builder.middlewares);
this.middlewares = List.copyOf(list);
它不是排出来的,是塞进去的。new 一个 ArrayList,先把 Shutdown 放第 0 位,再 addAll 你的,最后 copyOf 封死。Builder 的方法表里搜 graceful 和 shutdown,零匹配——没有开关。
GracefulShutdown 不参与排序,它是构造器硬塞的,order 给多大都没用。
必须最外的原因,下面这段是我的推断,不是字节码证明:停机信号要能穿。Ctrl+C 那一刻,信号从最外层往里走,穿过十一个内置、穿过你那个 50 的,最后落到 agent 上把账本存下来。它要是被谁挡在外面,优雅停机就成了优雅地丢状态。
- 同一个构造器里还有一行 bindStateSaver——停机存状态这活就是它干的。
- 推一句我还没验的:用户中间件给 order=999,按“不参与排序”推,它还是在索引 1。我实测的是 order=50 落在索引 1,999 那组没跑。
4.3 11 是默认集合:18+1 与 7 个条件类
我把 harness jar 的 middleware 包全列了一遍:18 个具体类,加 1 个接口。那个接口 HarnessRuntimeMiddleware 是空的,一个方法都没有,只用来给这批类打标签。
18 个里默认进场 11 个。剩下 7 个是条件类:
- PlanMode
- SandboxLifecycle
- Teams
- AsyncTool
- Subagents
- SkillCurator
- SkillUsage。
名字基本就是触发条件——开计划模式、挂沙箱、上团队、用异步工具、声明子代理、装技能仓库。
默认那 11 个管的是每个 agent 都躲不开的事:留痕、读人格、清上下文。后进场的 7 个管你选了才有的东西:计划、沙箱、团队。说白了,默认只兜底,不替你选型。
反方向也能减员:
- disableSubagents() 摘掉 DynamicSubagents
- disableCompaction() 摘掉 Compaction。
这两个我都跑过,数在 §5.3。
所以 11 是默认集合,会随配置变。把一次快照当常量背,换个配置就全对不上。
诚实补一句:七个条件类分别由哪个 builder 方法触发,我只核到两个——PlanMode 是 enablePlanMode(),Subagents 是 .subagent(...) 或 workspace 声明。另外五个是照类名和文档推的,没逐个跑。
4.4 三种“固定”的辨析
到这儿“固定”这个词已经被我用乱了。拆三种:
怎么判?跑一次 .disableCompaction(),再打一次端点:13 变 12,23 不变。清单变了,它就是成员不是常量。这条判据比背 13 个名字有用,因为下个版本的数字一定会变。
5、23 个工具与三个开关
5.1 八类工具:模型默认能干什么
同一个端点里读 getToolkit().getToolNames(),23 个。按用途分八类:
八类加起来 23,我一行没注册。前四类(记忆、会话、子代理、任务)都围着账本和协作转,后四类(文件、命令、网络、技能)是手脚。
也就是说,一个零配置的 agent,模型已经能列目录、读文件、改文件、grep、跑 shell、上网抓页面、查自己的记忆、派子代理、等异步结果。
这份清单在货代项目里,我第一反应是“得关几个”。execute 能跑任意命令,write_file 能改任意文件——本机试验无所谓,接内部系统前必须先把沙箱或 ASK 审批闸门(0006 的权限规则)架上。反过来,不想让模型碰记忆这套工具(读、写、会话历史),disableMemoryTools() 一次摘 6 个(3 个 memory_* 加 3 个 session_*),中间件一个不动。这组是我另写一个探针跑的,没进端点返回。
5.2 能力 = 中间件 + 工具:两问判断法
十二项能力和中间件是不是一一对应?我一开始默认是。做完集合差发现,映射根本不成立。
三个代表摆出来:
判断就两问:
- 模型要不要自己发起这个动作?要 → 必须有工具。
- 框架要不要在背后自动执行或拦截?要 → 需要中间件。
两个都“要”,成对;两个都“不要”,那是 core 的机制(状态持久化)或者网关(Channel)。
有个更快的问法:模型不知道这个能力存在,它还能工作吗?压缩和记忆冲刷能,所以它们只需要中间件;但查记忆、派子代理不行——模型得先知道有这回事,才谈得上用。
成本也不对称:工具进 schema,中间件不进。23 个工具的 JSON Schema 每轮都要塞给模型,这笔 token 是实付的;中间件对模型不可见,只花事件流包装的钱。所以“能不加工具就不加”是真约束,不是审美。拿 RAG 走一遍:
- 让模型自己决定查什么,是 Agentic RAG,得有 search_knowledge 工具;
- 每轮自动检索注入,是 Pipeline RAG,一个 onSystemPrompt 中间件就够,模型甚至不知道有检索这回事。
- 生产常见两样都上。
回到那个等式:“十二项能力 = 十二个中间件”错在哪?能力是打包,不是类。同一个能力可以只动工具(disableMemoryTools 减 6 个工具、0 个中间件),也可以只动中间件(disableCompaction 减 1 个中间件、0 个工具)。
5.3 三个开关的增删
三个变体我各 build 了一次,对基线做集合差:
重点在中间两列,它们各动各的。
enablePlanMode 一次加四个零件,disableSubagents 一次减七个,disableCompaction 只减一个。打包比例不固定——说明开关是零件级的,不是能力级的。
这里有一注我押错了。我先押的那一问是“哪个开关只动中间件不动工具”,我填了 enablePlanMode。对的是 disableCompaction。错的原因很直接:我以为计划模式是纯行为约束,一个中间件就够。
5.4 计划模式为什么带三个工具
计划模式听着最像纯行为:让模型先只读地想,把计划写下来,等人点头再动手。行为约束嘛,一个中间件拦一下写操作就够了。实际它有 3 个工具。
因为进计划模式、退计划模式、写计划文件,都是模型自己的动作。没有 plan_enter,它不知道自己现在能不能写;没有 plan_exit,它出不去;没有 plan_write,它连计划该写在哪儿、给谁看都不知道。
中间件只能干等。它能拦住写操作,但没法替模型发起“我要进计划模式”这个动作。这就是“能力 = 行为 + 手脚”的具体样子。只给工具不给中间件也白搭:模型能喊“我要进计划模式”,可没人拦它写文件,计划模式就成了一句口号。
6、状态三层与默认值翻转
6.1 调用内 / 跨调用 / 长期记忆
0008 那套 L1/L2/L3,到了整机里展开成三段存储,各归各的人管:
这张表跟 0008 的 L1/L2/L3 对得上,只是把 L2 拆成了两段:账本,和账本之外的沉淀。L3 还是空集。0006 的审批规则就在账本里(§3.2 说过),这里不重复。
要划清的一句:账本的持久化是 core 的活,Harness 没重写一遍。那张表最后一列写的是 core,不是中间件。
还有个容易看漏的半开状态:关掉记忆工具,记忆中间件还在。disableMemoryTools() 减 6 个工具、0 个中间件——模型不能再主动查记忆,但 MemoryFlush 那两层还在链上(索引 6、7)。它还会不会往盘上写,我没单独验;按“两半独立”推是会,但我没跑这一步。
6.2 裸引擎 null vs 整机 JsonFile
最反直觉的是默认值。0008 里我亲手 new JsonFileAgentStateStore 再 .stateStore(...),才做到重启接续;裸 ReActAgent 的 stateStore 默认是 null。
动作从“接上”变成了“换实现”。整机没在裸引擎外面套壳;它把你原来必须做的那一步默认做了。
图里三件事:stateStore 非 null,workspace 指到我给的目录,agentsMdPresent 是 false。
6.3 两条规律
- system prompt 每次运行重拼,运行内多轮共用。对你的结论不变——改 AGENTS.md,下次发消息就生效,不用重启。
- 压缩、记忆提炼、后台维护都过闸门,不是每轮都跑。每轮摘要一次,账算不过来。
第 1 条有实测口径:文档写“每轮”,0007 的日志不是——一次运行两轮推理,onSystemPrompt 只有 1 行,从 114 字符涨到 171。第 2 条是文档说法,压缩/记忆/维护的触发阈值我这一课没测,只确认它们不在默认装配的每轮路径上。
第 1 条在工程上的含义是:AGENTS.md 是热更新的。不用重启、不用发版,改完文件下一句话就按新口径答。第 2 条反过来提醒我:别指望“改完配置,下一轮立刻见效”——闸门没到点,它不动。
6.4 部署含义:家目录、共根、AGENTS.md
默认落 ~/.agentscope/state/<agentId>/。单机试验很方便,生产是个坑:同一台机器跑多个 HarnessAgent,它们共用一个根。要隔离两条路——显式 .stateStore(...),或者改 agentscope.state.home。
另一条告警:workspace 目录给了,里面没有 AGENTS.md,启动会打 AGENTS.md not found in workspace: ...(目录本身不存在时,先报的是另一条 Workspace directory does not exist)。这行字的意思很直白——23 个工具给了,人格一个没给。
所以“默认全含”不等于啥都不用干。反过来说,你只想要一个能读文件、跑命令的工具,不写 AGENTS.md 也能跑,只是它不知道自己是谁、按哪套单证口径说话。
7、边界:中间件管横切,控制流归引擎
7.1 五个收益与五个代价
“能力不改循环”是个赌注。赢的那面上面都在讲,输的那面也得摆上桌。
第三列该当选购条件读。你要做的事落在第三列任何一格,中间件就不是答案。
“插错层坏别人的事”举个具体的:在压缩之前滤一遍文本,压缩看到的就不是原文,后面所有依赖原文的判断都跟着歪。这是第 7 课“层级即视野”的推论。
7.2 控制流三档判据
我拿一个具体需求试:第 3 轮之后要插一段控制流,中间件够不够?
三行的分界线是“要不要动循环的推进”。改输入是相位内的事;挂起、恢复、换算法,都是循环本身的事。
行业里这件事有两种形态,我对照了几家官方文档,补一张表:
Claude Code 的权限规则是典型的门:拦得住,但不保存可恢复的 run state。要“换进程还能接着跑”,就必须是后者。
7.3 框架自己的反证:HITL 在 CallExecution
最硬的证据来自框架自己的选择。0006 的权限和 HITL 没做成中间件,做进了 core 的 CallExecution——准确说,跨请求恢复的那部分在 core;单独的规则门可以横切(§7.2 的 Claude 权限就是)。ASK 要挂起整个循环等人,人回来再恢复,这是控制流。
我第 6 课的时候还觉得“审批拦在 onActing 上不就行了”。onActing 能拦住调用,但拦不住“等”。中间件可以决定放行还是抛异常,但它没有挂起/序列化循环状态的把手——它能拦,不能等。
“等”这件事看着轻,实际是状态:谁在等、等的是哪个中断 id、人回了之后从哪继续。这些得存进 AgentState,得能被下一次请求捞出来。中间件碰不到这层。
7.4 货代推论
放到订舱里:审批如果只是“金额超 5 万问一句人”,ASK 规则够用。如果是“超 5 万走经理、超 20 万走总监、欧线再加一道合规”,那是按金额和航线动态改流程,流程本身在变。
中间件做不了。用 core 的 interrupt 加自己的状态机,或者干脆上 Service 的 Workflow。
我现在的判断:横切的、每个工具都要过一遍的(脱敏、审计、限流),中间件;要改“下一步做什么”的(多级审批、退回重来),状态机。
8、零件 vs 整机:本质区别与四种运行形态
8.1 对照表
先把话说准:“整机”不是 AgentScope 独有。我原来那张“左边只有零件”的表,只对 Java 库核心层成立;放到产品层,2026 年已经有好几台整机:
所以在 Java 库核心这一层,差别才是“默认装配厚度”。Spring AI 2.0 核心有 Advisors、ChatMemory、tool calling、observability;ToolCallingAdvisor 管完整工具循环。压缩、子代理、技能不在核心里,在 spring-ai-session / spring-ai-agent-utils 社区项目(target 2.1)。ShellTools 官方明说无沙箱、无技能级人工审批。LangChain4j 的 ChatMemory 只有窗口淘汰;langchain4j-agentic 有 Supervisor/HITL,但整体实验性。沙箱、计划模式、人格文件,官方目录里没见。
前两行里,循环打平——这就是“循环是商品”;历史不等价:ChatMemory 只管消息,AgentState 是全快照。第三行往下,左边核心开始写“无”,但要注意两件事:一是社区/实验模块在补位,不能写成“Spring AI 什么都没有”;二是 AgentScope 这一列里,会话表在 AG-UI starter、通道在网关,不算 Harness 本体。
我第一遍把左边写得太满,核官网时发现 Spring AI 的 Advisor 链跟第 7 课的钩子是同一个东西——adviseCall 里 nextCall 前后插逻辑,能短路;连 order 的方向都反着来(它数值小的在外层)。我核完官网才敢说:左边有切面,没有整机——但这是 Java 库核心层的事,不是行业的事。
到了产品层,整机已经不止一台。AgentScope 的差异是默认装配厚度、Java/Spring 落点,以及装配契约:能力拆成中间件 + 工具、顺序固定、权限在 core、状态默认开。这张表我改了三遍,越改越短——最后留下的,都是能一句话说清的差别。
8.2 四个能力域的覆盖
换个切法,把活分成四块:
- 执行与编排:循环、工具、子代理、计划。Harness 覆盖。
- 上下文与状态:压缩、卸载、记忆、账本。Harness 覆盖。
- 行动与反馈:文件、命令、网络、沙箱。Harness 有,但沙箱要配置后才有。
- 质量与验证:做对没有,谁判?没人替你。
前三块它给了默认答案,第四块没有。Verifier 还是你的:订舱单填得对不对、箱号校验过不过、客户要的免柜期有没有写进去——框架不懂你的业务,判不了。
识别法子很土:数你要自建几样,还要自己定义它们怎么通信。数到三样以上,你买的是零件。
8.3 诚实边界
除了 Verifier,还有三笔账它明确不接:Runtime 的队列、租约、故障恢复;Sandbox 的运维(镜像、资源、逃逸);Platform 的多租户、发布、观测、配额。这些对照也只能证明“别家也有整机”,不能证明 AgentScope 更强——强弱要看默认中间件的覆盖面和边界,不能看清单长度。
我自己也没验过的两条:多副本共用一个 store 时的并发写,我只在单 JVM 里跑过;Docker 沙箱我没起过容器。这两条等我真上多副本、真挂沙箱再说,现在写“应该没问题”是骗人。
8.4 四种形态与货代落点
同一套 HarnessAgent,换个部署位置就是另一种形态。官方的问法固定四句:调用从哪发起、状态存哪、Runtime 和沙箱谁提供、你还得补什么。
选型四问就是表头那四列。workbench 现在在第二行:Spring Boot 里调 agent,RuntimeContext 从 AG-UI 请求来,stateStore 是本地 JSON 文件。
什么时候往下一行走?我的信号是“客户挂断电话之后,任务还得继续跑”。出现这个场景,才需要 Worker + 共享 store + 沙箱。没出现就上长任务,是给自己造运维。
9、收口:带走三句与临走三问
回到开头那个端点。13 个中间件、23 个工具、一个非 null 的 store——这不是空壳被配置之后的样子,是整机的出厂状态。但“整机”不是它独有:产品层已经有好几台,差别在默认装多厚、装在哪个栈上。
带走三句:
- 整机不是空壳,默认全含,要关不要开。
- 能力 = 中间件(行为)+ 工具(手脚);两半独立开关,引擎不换。
- 固定的是顺序,不是
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
summary: An unarmed HarnessAgent, with 13 intermediates, 23 tools, and a home directory. From an anatomical endpoint, this paper disassemblys the list: which are fixed and which change with configuration; why are 12 capabilities not 12 intermediates; where is the status after the default flip? It ends up at the boundary of the intermediate model: it cuts across the middle, controls the flow to the engine — others have the whole machine, the difference being how thick it is and on which stack it is.
About 7328 words read about 18 minutes
One, opening: a HarnessAgent who doesn't deserve anything.
Class 9 began with a read-only endpoint: GET / workbench/harness/anatomy. It only assembles, does not call -- the model is in place for fake and one business tool is not registered. The following figures are for AgentScope 2.0.3.
HarnessAgent.builder()
.name("anatomy-probe")
.model(new FakeChatModel())
.workspace(Path.of("data/harness-probe/workspace"))
.middleware (new ProbeuserMiddleware()) / /order=50, playing "The One You Write"
.build();
FakeChatModel is a fake model of my project; ProbeuserMiddleware has only one line of implements Middlewarebase, order() returns 50.
Three numbers of peer returns:
middlewares 13 (core 1 + user 1 + harness 11)
tools 23
StateStore JasonFileAgentStateStore
The response body is JSON: baseline.middlewares[], baseline.tools[], baseline.stateStore, and variants[]; I removed the three lines above.
Before opening this end, I put four bets: one in the middle, zero in the row, 20 on the tool, and StateStore is nul. One note only - The number of tools is 23.
- My imagination about Harness was empty: power opens on demand, nothing is configured. In the opposite direction, the default is all. All you have to do is turn off.
- And half the misunderstanding came out of hand: I thought 13 of them were officially called "twelve capabilities." This equation is not valid either, § 5.2. There are three remaining questions: 13 who pretends, who crucifys, what changes.
Want to read it for yourself:.../mvnw Spring-boot: run, then curl 127.0.0.1: 8080/workbench/harness/atomy. Without the project, the first two lines of GetDelegate(), GetClass() and get Middlewares().size() can be verified. The probe points to the data/harness-probe/state, completion close() - without meaning, it writes an account book to your home directory. Without service, Lab.html plays "Anatomy Failed: Is Service Running? "; End 404 Check ports and path-prefix.
2. Clear the border: Harness, not the engine.
2.1 Harness is an engineering system outside the model
The official definition is a long sentence, which I copied:
Harness = outside the model, organise context, capabilities, state, environment and control mechanisms around Agent Loop and translate model judgement into code/ configuration/ execution logic for an implementable, recoverable, verifiable task process.
The four words “other than the model” are focused. None of this stuff goes into model weights, all Java code and directory files.
It's not longer than prompt. You put 5,000 words into AGENTS.md, and context was the blast or the explosion; it really kept context from spilling. It was Company Midleware cutting off the flow of events and summary, and that was the code moving.
Read figure three: who packs who -- the middle chain loops ReAct around the whole; who goes by who -- the loop-out model, down-to-Toolkit execution tool; where the status is -- the books are store back, the personality and the file are workingspace. The rest of the sections are tearing down parts of this picture.
2.2 Three easily mixed borders
- It is a certainty logic beyond multiple rounds of reasoning. prompt is only one of the inputs generated in a given round; the plan, permission check, error process, validation is not in that word.
- It's "Agent how to work" on this floor. The CLI of Coding Agent carries its own set of hosting platforms that sell Harness along with executive services; it describes a system layer, not a product form.
- It's divided with Runtme, Sandbox. Harness decides what next step is looking at and what it is allowed to say; Runtme pipe process, queue, schedule; Sandbox handles files, networks, processes.
All three borders point to the same place: the line you want to change in the middle of the night.
2.3 Five Levels of Liability Boundary: Problem first
Officially, responsibility is cut to five levels. I'm copying a table with one of the symptoms I've encountered or seen on each floor:
I'll give you this watch, not the back. context blew the compression, and Worker hung up on the queue and overwritten the documents to see the sandbox. The three modifications fell in three places, and none of the models were resolved.
The definition of attribution is useful.
Three principles.
The three principles are one chain: the ability to hang on the loop, the ability to speak between only three objects, and the sequence of assembly is set by framework.
3.1 Capability superimposed without recycle
Two lines show what's in the shell:
agent.getDelegate().getClass() // io.agentscope.core.ReActAgent
agent.getDelegate().getMiddlewares().size() // 13
Get Delegate() takes out the engine inside, which is the same thing as Spring's agent: every way you call on your shell, you end up on the packaged machine. HarnessAgent doesn't have his own loop.
13 intermediates on five hooks of Rect cycle. Class 7 untie hooks, and here's the conclusion: circulation is a commodity, capacity is difference.
Why goods? React cycles are similar in any frame -- a while, a model call, a round of tools. What really divides is how context is pressurized, how the state is present, how dangerous moves are stopped. All three things are transected and naturally fit to hang the middle. Since the cycle is a commodity, the official list of 12 is its capabilities:
This watch is not for the back. It shows me how many of the twelve items, even the middle items, can't be touched in the default assembly -- in core, Channel in the gateway. Compression and unmounting, though attached to the chain, are only two rows of 8th and 9th bits, and the document cannot match the number.
Note: This table is cut by “capacity”; § 8.1 is replaced by a set of cut methods (whether built-in or not), and the two tables are not read in tandem.
3.2 Three Shared Objects
There is no transfer between capacities. That's the premise: memory shouldn't know the compression, compression shouldn't know the sub-agents. They only recognize three objects, one in each:
In kind: The rules of approval of 0006 survive across requests, which I thought was a middle item. Nope. The rules are in AgentState.permissionContext, drops the book, next call comes back with the book, and the middle is just reading it.
The three objects are three plugins, who want to use electricity and who does so; the plug-in shape is three, not the private interface between the intermediates.
3.3 Order of assembly: fixed order, not membership
Officially:
The internal middleware registration order is fixed, and you add it to the front.
This is the first time I've written directly, and the second time I've found myself misreading “the front line”. The complete list is read at § 4.1, and here's the conclusion: my order=50 rank is in the first -- running before Harness, but after GracefulShutdown in core. So the exact caliber of the "front" is: before the inner, after the Shutdown.
Order sorts only between users' intermediates. All 11 of them are order = 1, who's first and who doesn't? Infer, that's the order of registration for the frame's death. The ones you plug in, you line up in descending order, fill up the whole body in front.
Index 0 is the most external, chain from the end of the list - the old rules of Middleware Chain in lesson 7. The two pits are left in the following section: Index 0, why is the one crucified? 2 to 12. Is this 11 lines constant? The answer to the second question is, and is, more than I thought.
Where are the 4 or 13 intermediates coming from?
4.1 Default list and five clusters
I looked at it for a while. In the 13th row, 12 rows of order are all one, and the only line I stuff is 50-order is barely working in this chain.
0 order=1 core GracefulShutdownMiddleware
1 order=50 user ProbeUserMiddleware
2 order=1 harness AgentTraceMiddleware
3 order=1 harness WorkspaceContextMiddleware
4 order=1 harness AtPathExpansionMiddleware
5 order=1 harness TranscriptMiddleware
6 order=1 harness MemoryFlushMiddleware
7 order=1 harness MemoryMaintenanceMiddleware
8 order=1 harness CompactionMiddleware
9 order=1 harness ToolResultEvictionMiddleware
10 order=1 harness InboxMiddleware
11 order=1 harness DynamicSubagentsMiddleware
12 order=1 harness HarnessSkillMiddleware
Read: 0 is core, 1 is you, 2 to 12 is Harness built in.
I'm in five groups by name:
- One mark: AgentTrace.
- Three personalities and paths: WorldspaceContext, AtPathExpansion, Transcript
- Memory and context four: Memoory Flush, Memoory Maintenance, Company, ToolResultEviation
- Collaboration: Inbox, DynamicSubagents
- One skill: HarnessSkill.
After reading the eleven names, one thing is clear: the default assembly has no pattern, no sandbox, no team. They don't come in until you ask.
And one more thing I saw when I read it the second time: Index 6 and 7, one by one, one by one, one by one, and Index 8 and nine by one, one by context, one by one, put the big results on the plate. And it's usually two steps next to the same thing, so the list of neighbors can be read in order -- you don't have to look at the files, you can guess who's working with who.
Can you turn it off: I've only tested two disables. I don't have a single test. Don't take my word for it.
4.2 GracefulShutdown Why is it always the outermost?
Javap hits the Private Constructor (Builder, Toolkit) of RectAgent, with four lines. The command is javap-p-c-classpat anentscap-core-2.0.3.jar io.agentscap.core.ReactAgent; no -c only for signatures:
List<MiddlewareBase> list = new ArrayList<>();
list.add(new GracefulShutdownMiddleware(GracefulShutdownManager.INSTANCE));
list.addAll(builder.middlewares);
this.middlewares = List.copyOf(list);
It's not coming out, it's coming in. A new ArrayList, put Shutdown in position 0, then add All yours, and finally copyof seal. Builder's method sheet search Graceful and shutdown, zero match - no switch.
GracefulShutdown does not participate in the sorting, it's built hard, order is useless.
The reason must be the outside: this is my inference, not byte code: the stop signal is fit. Ctrl+C, that moment, the signal went from the outermost, through eleven inner, through your 50s, and ended up depositing the books on angent. If it is blocked from the outside, the elegance shut down becomes elegance.
- There's a line in the same constructor -- bindstateSaver -- that's what this job is.
- By the way, I haven't tested it yet: the user's intermediate is given order = 999, pushed by "not participating" and it's still index 1. And what I've measured is that the order = 50 landed in index 1,999, and the group didn't run.
4.3 11 is the default collection: 18+1 and 7 conditions Category
I'm going to reset the middleware package: 18 specific categories, plus 1 interface. The interface, Harness Runte Middleware, is empty, there's no way to label it.
The default entry for 18 is 11. The remaining 7 are in conditional category:
- PlanMode
- SandboxLifecycle
- Teams
- AsyncTool
- Subagents
- SkillCurator
- SkillUsage。
The name is basically a trigger - a plan model, a sandbox, a team, a walker, a statement agent, a skill warehouse.
By default, the 11 things that matter to each angent cannot be avoided: leaving marks, reading people, cleaning up context. Seven of the things you choose to do in the back field: plans, sandboxes, teams. It's a lie. It's a secret.
The opposite direction can be reduced:
- Disable Subagents() Remove DynamicSubagents
- Disable Command() remove Company.
I've run both, counting § 5.3.
So 11 is the default assembly, which changes with the configuration. You can't do anything with that.
Honesty adds: I have only two -- PlanMode is capable PlanMode, Subagents is.subagent(...) or workspace statements. The other five were pushed by category and by file, not one by one.
4.4 Three “fixed” resolution
I've used the word "fixed" to get here. Three types:
How? Run once. The list has changed, and it is not a constant. This rule is more useful than 13 names, because the next version will change.
5, 23 tools and 3 switches
5.1 Eight types of tools: what models can default on
Reads getToolkit().getToolNames(), 23 in the same endpoint. Eight categories by purpose:
Eight classes combined 23, I'm not registered. The first four categories (Memory, Sessions, Sub-Agents, Tasks) are surrounded by books and collaborative transfers, while the latter four categories (Documents, Commands, Networks, Skills) are manual.
In other words, a zero-configured agent model has been able to catalogue, read files, change files, grep, runshells, grab pages online, check your memories, pie agency, and so on.
This list is in the freight forwarder's project, and my first reaction is “a few”. Execute can run any order, write file can change any file - this machine test doesn't matter, but the sandbox or ASK clearance gate (permission rule of 0006) must be mounted before taking over the internal system. Conversely, the model does not want to touch the memory set (reading, writing, talking history), disable memoryTools(), six at a time (3 memory * plus 3 sessions *), one at the middle. I wrote another probe, didn't come back.
5.2 Capability = Intermediate + Tool: Two Question Judgements
Are the 12 capabilities and the intermediates a match? I agreed at first. The map doesn't work.
Three delegates put it out:
Two questions to judge:
- Do you want to start this on your own? To poach, there must be tools.
- Should the frame be automatically executed or intercepted behind the back? To poach, you need the middle.
Both are "yes" and pair; both are "no" and that is the core mechanism (state permanence) or gateway (channel).
There's a faster question: does the model work without knowing it exists? Compression and memory brushes, so they only need intermediates; but memory checks, pie agents don't work -- models need to know what's going on before they work.
Costs are also asymmetric: tools go into schema, middle is not. JSON Schema, 23 tools, each wheel of the tool, will be plugged into the model, and this token is paid for; the intermediate is invisible to the model and only spent on event flow packaging. So, "can't do it without tools" is true, not aesthetic. Take RAG and go over:
- Let the model decide what to look for.
- Every round of automatic search injection is Pipeline RAG, one on SystemPrompt, and the model doesn't even know there's a search.
- Both are common.
Back to the equation: "Twelve Capabilities = Twelve Intermediates" What's wrong with that? Capability is packing, not class. The same ability can only move tools (disable memoryTools minus 6 tools, 0 intermediates) or only intermediates (disable Command minus 1 middle, 0 tools).
5.3 Adds to or deletes from three switches
The three variants I've done for each of the bilds, and we've made a pool difference to the baseline:
The focus is on two rows in the middle, each moving.
Allowable PlanMode plus four parts at a time, disable Subagents minus seven at a time, disable Commission minus only one. Packing ratios are not fixed - it is stated that switches are part-scale, not capacity-level.
Here's a bet I made wrong. The question I put in first was, "What switch does not move the medium?" I filled out an enabling PlanMode. That's right. The reason for the error is straightforward: I thought the plan model was purely behavioural, and a middle is enough.
5.4 Why the plan model with three tools
The plan model sounds like pure behavior: let the model read only, write down the plan, and wait for people to nod. Behavioural restraint, a middle note to stop writing. It actually has three tools.
Because it's a model of entry, a model of withdrawal and a plan document, it's a model of its own. No plan enter, it doesn't know if it can write now; no plan exit, it can't get out; no plan write, it doesn't even know where the plan should be written and who to see.
The intermediate can only wait. It can stop writing, but it can't start the "I'm going into plan mode" for the model. This is what it looks like to be able to = behavior + hands and feet. It's not only a tool without a middle: a model can say, "I'm going into the plan model," but no one stops it from writing, and the plan model becomes a slogan.
6, status 3 and default flip
6.1 Call Inside / Cross Call / long-term memory
The L1/L2/L3 set, which is used in three parts of the machine, is divided into persons:
This table matches the L1/L2/L3 of 0008, and only split L2 into two sections: the books, and the sediments outside the books. L3 is still empty. The rule of approval of 0006 is stated in the books (§3.2), and is not repeated here.
The line to be drawn out: the permanence of the books is the work of core, Harness not rewritten. The last column of that table says core, not the middle.
And there's a half-opener that can be easily missed: turning off memory tools, and the middle is still there. Disable memoryTools() minus 6 tools, 0 intermediates - models can no longer proactively check memory, but the two layers of memory Flush are still on the chain (index 6, 7). It's not going to be written on the plate, I didn't do it alone; it's going to be two-and-a-half independent, but I didn't do it.
6.2 Naked engine full of vs. Jason File
The most counterintuitive is the default value. New Jason FileAgentStore, again.stateStore(...) to restart; nudistReactAgent's stateStore default is nul.
The action changes from " Connect " to " Swap." The whole machine didn't fit the shell outside the naked engine; it did the one thing you had to do.
Three things in the chart: stateStore is not full, workspace points to the directory I'm giving, including MdPrecent is false.
6.3 Two laws
- System prompt, run multiple rounds at each time. No change in your conclusion — change AGENTS.md, the next message will take effect without reopening.
- Compression, memory extraction, backstage maintenance, not every round. One round of summary, one time.
Article 1 has a measured caliber: the log of document writing "per wheel", 0007, is not - running two rounds of reasoning at a time, onSystemPrompt has only one row, rising from 114 characters to 171. Article 2 is a document statement, and the trigger threshold for compression/memory/maintenance is not measured in my class, only to confirm that they are not on each path of the default assembly.
Article 1 in engineering means that AGENTS.md is a hot update. The next sentence of the document will be replaced by a new calibre without reopening or issuing. Article 2, in turn, reminds me not to expect “to change the configuration and the next round will be effective” — the gate will not be there and it will not move.
6.4 Deployment: Directory, Commons, AGENTS.md
Default set ~/.entscope/state/<entId>/. A single machine test is convenient and production is a pit: the same machine runs multiple HarnessAgents, which share one root. Isolate two paths - visible.stateStore(...), or change it.state.home.
Another alarm: the workspace directory gave it, which did not contain AGENTS.md not found in workspace:... (Workspace directory does not exist first). It's very clear -- 23 tools, none.
So "the default is all" means nothing. Instead, you want a tool that reads documents, runs orders, doesn't write AGENTS.md, but it doesn't know who it is or what kind of document it speaks.
7 BORDER: Intermediate transversal, control flow to engine
7.1 Five gains versus five costs
“Capacity does not change the cycle” is a bet. The winning side is all over it, and the losing side has to be on the table.
Thirdly, the conditions for the selection are read. The middle is not the answer.
"Playing the wrong layer of bad people's things" is specific: filtering the text before compression, which is not the original, and all the subsequent judgements relying on the original are crooked. This is the inference of class 7 "Standing is Vision".
7.2 Control 3rd tranche
I'm going to try a specific need: is there enough middle after the third round?
The dividing line for the three rows is "Whether or not to move the cycle. " The re-entry is within the phase; the hang-up, restoration, convert method is a matter of the cycle itself.
There are two forms of this thing in the industry, and I've checked several official documents to add a table:
Claude Code's permission rule is typical of doors: can stop, but does not save the RUN state. For the process to be “replaced and run”, it must be the latter.
7.3 Frame Your Counterfeit: HITL in CallExact
The strongest evidence comes from the framework itself. The permission and HITL of 0006 did not form the intermediate and entered the CallExaction of core - precisely the part of the request for restoration is core; the separate door of the rule can be crossed (Claude permission of § 7.2). ASK is going to hang the whole cycle of people, and people come back and recover, and that's the control stream.
And I thought, in class 6, "Approving onActing." OnActing can stop calls, but can't stop "waiting." The middle can decide whether to release or throw an anomaly, but it does not have the handle to hang up/sequencing cycle -- it can stop, cannot wait.
“Waiting” looks light, actually, is the state: who's waiting, who's waiting, who's waiting, id, who's going to go on. These have to be stored in Agent State and will have to be recovered by the next request. The middle doesn't touch this layer.
7.4 Arguments of the freight forwarder
Put it in the booking room: the ASK rule is sufficient if the approval is merely a “over $50,000 question”. If it's “over 50,000 goers, over 200,000 goers, plus one more compliance line”, it's a dynamic change of process in terms of amount and course.
Could not do the middle. Add your own status machine with core's interrupt, or simply go to Service's Workflow.
I now make my judgment: the one that cuts across every tool (sensitization, auditing, rate limit), the middle; the one that changes “What Z next step does” (multi-level approval, back-to-back), the status machine.
8. Parts vs. whole machine: the difference between nature and four operating forms
8.1 Comparison Tables
First of all, "The whole machine" is not unique to ArgentinaScope. My original "parts only on the left" watch was set up only for the Java reservoir core; it was placed in the product layer, and in 2026 there were already several whole machines:
So at the core of Java Library, the difference is the "default configuration thickness". Spring AI 2.0 cores are Advisors, ChatMemoory, Tool calling, Observability; ToolCallingAdvisor complete tool loops. Compression, sub-agents, skills not in the core, in the Spring-ai-session / Spring-ai-agent-utils community project (target 2.1). ShellTools officially stated that sandless boxes and unskilled manual approvals were available. ChatMemory of Lang Chain4j has only window phase-out; Langchain4j-agent has Supervisor/HITL, but is experimental as a whole. Sandboxes, plan models, personal documents, not in the official catalogue.
In the first two lines, the cycle leveled -- that's "cycle is a commodity" -- and the price difference between history and history: ChatMemory, just the news, Agentstate, is a full snapshot. The third line goes down, the left core begins to write "Nil," but two things are to be noted: the community/experiment module is in place and cannot be written as "Spring AI "; the second is AgentScope, where the session table is at AG-UI Starter, the channel is at the gateway, not Harness.
The first time I wrote the left too full, the nuclear officer network discovered that the Advisor chain of Spring AI was the same thing as the hook of class 7 -- the logic of nextCall in an adviseCall, short-circuiting; even the direction of the Order is the opposite. I don't think I'm going to be able to say that there's a cut on the left, there's no machine -- but it's the core of the Java Library, not the industry.
There's more than one machine on the floor. The difference between AgentScope is the default configuration thickness, Java/Spring drop point, and assembly contract: capability to break down to intermediate + tool, sequence to fix, permission to core, status default open. I've changed this watch three times, and it's shorter — and the last thing I leave is a clear line of difference.
8.2 Coverage of the four capability domains
Switch it and split it into four pieces:
- Implementation and organization: circulars, tools, sub-agents, plans. Harness Overwrite.
- context and Status: Compression, unloading, memory, account book. Harness Overwrite.
- Actions and feedback: documents, orders, networks, sandboxes. Harness has, but the sandbox needs to be configured.
- Quality and certification: No right, who? No one for you.
The first three pieces give the default answer, the fourth one doesn't. Verifier is also yours: the bookings are correct, the box numbers are unverified, the client's request for an exemption period is not included -- the frame doesn't understand your business, it can't.
The way to identify is so crude: you have to build yourself a few things and define how they communicate. On the count of three, you buy spare parts.
8.3 An honest border
In addition to Verifier, there are three accounts that it clearly does not: Runtme ' s queue, lease, failure recovery; Sandbox ' s transport (image, resources, escape); Platform ' s multi-tenant, publish, observation, quota. These contrasts can only prove that “others also have a complete set” and that AgentScope is stronger — it depends on the coverage and boundaries of the default intermediate and does not look at the length of the list.
I didn't check two of them myself: a copy of the same copy as a store, and I ran in a single JVM; I didn't pick up the box in Docker's sandbox. These two articles are a lie when I have copies and sandboxes.
8.4 Four forms and cargo landing points
The same HarnessAgent, a different deployment location, is another form. The official question is fixed in four sentences: from where to call, where to put it, who to supply Runtme and sandboxes, and what to add.
The selection four is the four rows of the table. Workbench is now in the second line: Spring Boot tunes anent, RuntimeContext requests from AG-UI, stateStore is a local JSON file.
When do we go down? My signal is “the mission has to continue after the client has hung up the phone”. This scene is needed to share store + sandboxes. If you don't show up, it's a long mission.
Concluding: taking three sentences and leaving three questions
Back to the beginning end. 13 intermediates, 23 tools, a non-full store... This is not what an empty shell looks like when it is configured, but the whole machine is out of the plant. But the whole machine is not unique to it: there are already several layers of products, different from the one that is built by default in thick and on which vault.
Take three sentences:
- The whole machine is not empty, it's all by default.
- Capability = intermediate (behaviour)+ tools (hands and feet); two-and-a-half independent switches, no change in engines.
- The order is fixed, no.
Auto-fetched and translated by AI Atlas. All rights belong to the original author.