【AI Agent】面向 Java 工程师的Claude Code Harness 学习指南

面向 Java 工程师的 Agent Harness 学习指南

读者定位

本文面向有以下背景的工程师:

  • 熟悉 Spring Boot
  • 做过微服务、MVC、AOP、权限、审计、网关、插件化
  • 理解常见设计模式
  • 还没有系统做过 Agent Runtime

本文不是"Claude Code 源码导游",而是借助 Claude Code / Open-ClaudeCode 这类项目,抽取其中最重要的工程思想,帮助你建立 Agent Harness 的架构认知。


1. 什么是 Harness

1.1 不要把 Agent 理解成"一个 Prompt"

很多人第一次看 Agent,会把它理解成:

  • 一个大 Prompt
  • 加几个 Tool
  • 再接一个聊天界面

这不够准确。

真正的 Agent 系统,尤其是 Claude Code 这类 coding agent,核心不是 prompt,而是 Harness。

1.2 Harness 的定义

Harness 可以理解为:

围绕 LLM 的执行外壳,用来把"文本生成"升级成"可控任务执行"。

它通常负责:

  • 接收用户输入
  • 组装 Prompt / Context
  • 发起模型调用
  • 解析模型返回的 text / tool_use
  • 执行工具
  • tool_result 再写回上下文
  • 控制多轮循环继续或终止
  • 保存状态、记忆、会话
  • 做权限、安全、审计、确认
  • 提供插件、子 Agent、外部工具接入能力

1.3 Java 类比

如果用 Java 的眼光看,Harness 很像下面这些组件的组合体:

  • WorkflowEngine
  • CommandBus
  • PolicyEngine
  • SessionStateStore
  • PluginContainer
  • IntegrationGateway

如果 Spring MVC 是"请求驱动的控制流",那么 Harness 更像"状态驱动的推理执行引擎"。


2. Claude Code 这类系统的整体架构

从工程上看,这类系统大致可以拆成 6 层。

2.1 交互入口层

负责接用户输入,可能是:

  • REPL
  • CLI
  • IDE 集成
  • SDK
  • 远程桥接

这一层不重要,重要的是所有输入最终都会落到同一个 Harness Loop。

Java 类比:

  • Controller
  • CLI Adapter
  • SDK Facade

2.2 Harness Kernel

这是系统内核,负责:

  • 接收当前会话状态
  • 组装 Prompt / Context
  • 调用模型
  • 解析 Tool Call
  • 执行 Tool
  • 更新上下文
  • 控制循环

Java 类比:

  • HarnessLoopEngine
  • TurnStateMachine

2.3 Tool Runtime

这是 Agent 的"命令执行层",负责:

  • Tool 的注册与发现
  • 输入 Schema
  • 校验
  • 权限
  • 执行上下文
  • 结构化返回结果

Java 类比:

  • Command Pattern
  • Registry Pattern
  • Chain of Responsibility

2.4 State / Memory / Context

这是 Agent 不会"每轮失忆"的关键层,负责:

  • 会话消息保存
  • transcript 持久化
  • 项目规则注入
  • 工作记忆
  • 长上下文裁剪和压缩

Java 类比:

  • Session Aggregate
  • Event Store
  • Snapshot / Compaction Service

2.5 Governance Layer

负责把系统从 demo 提升到工程系统:

  • Hook
  • 权限控制
  • 高风险确认
  • 安全策略
  • 审计能力

Java 类比:

  • Filter
  • Interceptor
  • AOP
  • Approval Workflow

2.6 Extension Layer

包括:

  • Subagent
  • MCP
  • Plugin
  • Remote Bridge

这层不是内核本身,但对平台化很重要。


3. 主执行循环(Execution Loop)

3.1 核心思想

Agent 不是"一次模型调用",而是一个循环:

  1. 拿到当前消息和上下文
  2. 组装 Prompt
  3. 调用模型
  4. 解析返回内容
  5. 如果有 Tool Call,就执行 Tool
  6. 把 Tool 结果写回消息
  7. 再次调用模型
  8. 直到完成或中断

3.2 执行链路

text 复制代码
用户输入
-> 输入处理
-> Prompt / Context 组装
-> LLM Gateway
-> Assistant content blocks
-> 解析 tool_use
-> Tool Runtime 执行
-> 生成 tool_result
-> 追加回 messages
-> 循环继续
-> 结束 / 中断 / 达到上限

3.3 这部分在 Open-ClaudeCode 中的典型锚点

  • 主循环:query.ts
  • 会话入口:QueryEngine.ts
  • Prompt 组装:queryContext.ts
  • 模型调用:claude.ts

3.4 Java 伪代码

java 复制代码
public class HarnessLoopEngine {

    public TurnResult runTurn(SessionState session, UserInput input) {
        ProcessedInput processed = inputProcessor.process(input, session);
        PromptEnvelope prompt = promptAssembler.build(session, processed);
        TurnState state = TurnState.start(session.getMessages(), prompt);

        while (true) {
            LlmResponse response = llmGateway.generate(state);
            AssistantMessage assistant = response.toAssistantMessage();
            state.appendAssistant(assistant);

            List<ToolCall> toolCalls = assistant.extractToolCalls();
            if (toolCalls.isEmpty()) {
                session.replaceMessages(state.getMessages());
                return TurnResult.completed(assistant);
            }

            List<ToolResultMessage> results =
                toolRuntime.execute(toolCalls, state.getToolContext());

            state.appendToolResults(results);

            if (state.exceedsMaxTurns()) {
                return TurnResult.maxTurns();
            }
            if (state.isCancelled()) {
                return TurnResult.cancelled();
            }
        }
    }
}

3.5 Java 工程师要学到什么

  • 这不是 Controller,而是状态机
  • Tool 调用不是附属逻辑,而是循环的一部分
  • Loop 的终止、中断、重试、压缩都必须由 Harness 管

4. Tool Runtime

4.1 Tool 不是普通函数

在成熟 Agent 里,Tool 不应该只是一个 execute() 方法。

Tool 至少要有:

  • 元数据
  • 输入 Schema
  • 校验逻辑
  • 权限要求
  • 执行上下文
  • 返回结果映射

4.2 Tool Runtime 的典型组成

  • ToolDefinition
  • ToolRegistry
  • ToolExecutor
  • ToolPermissionService
  • ToolInterceptorChain
  • ToolResultMapper

4.3 Open-ClaudeCode 的锚点

  • Tool 抽象:Tool.ts
  • Tool 注册:tools.ts
  • Tool 执行:toolExecution.ts
  • Tool 调度:toolOrchestration.ts

4.4 Java 类比

这部分最像:

  • Command Pattern
  • Strategy + Registry
  • Policy Guard
  • Adapter

4.5 最小抽象示例

java 复制代码
public interface ToolDefinition<I, O> {
    String name();
    JsonSchema inputSchema();
    boolean isReadOnly(I input);
}

public interface ToolHandler<I, O> {
    ToolExecutionResult<O> execute(I input, ToolContext context);
}

4.6 为什么这层很关键

没有 Tool Runtime,Tool 很快会退化成:

  • 到处散落的函数
  • 没有统一权限
  • 没有统一审计
  • 没法稳定接入模型

有了 Tool Runtime,模型只是在"发出请求",真正掌权的是 Harness。


5. State / Memory / Context

5.1 为什么 Agent 不会每轮失忆

不是因为它有某个神秘 Memory SDK。

而是因为系统做了三件事:

  1. 保留会话消息
  2. 每轮重新注入稳定上下文
  3. 对长历史做压缩而不是简单丢弃

5.2 会话记忆

最基础的 memory 就是当前会话的 messages

下一轮请求继续带上:

  • 之前的 user message
  • 之前的 assistant message
  • 之前的 tool_result

那 Agent 就有了短期记忆。

Java 类比:

  • SessionState.messages

5.3 Transcript 持久化

成熟 Agent 会把消息落盘或入库,这样进程重启还能恢复。

Open-ClaudeCode 的锚点:

  • transcript 存储:sessionStorage.ts

Java 类比:

  • append-only event log
  • conversation_events

5.4 工作记忆

工作记忆不是自然语言摘要,而是运行时状态,例如:

  • 已读过哪些文件
  • 当前 turn 数
  • 已调用哪些 skill
  • 当前任务或 todo
  • 某些中间缓存

Open-ClaudeCode 的锚点:

  • 会话状态:QueryEngine.ts
  • AppState:AppStateStore.ts

5.5 项目级规则和稳定上下文

Claude Code 这类系统不会只依赖聊天历史。

它每轮还会重新注入:

  • system prompt
  • 项目规则
  • CLAUDE.md
  • skills
  • git 状态
  • 当前日期

Open-ClaudeCode 的锚点:

  • 上下文:context.ts
  • Prompt 组装:systemPrompt.ts
  • 项目规则:claudemd.ts

5.6 长上下文裁剪 / 压缩 / 摘要

如果任务足够长,上下文一定会爆。

所以成熟 Harness 不会简单做"截断",而是做:

  • tool result 预算控制
  • snip / trim
  • microcompact
  • autocompact
  • collapse
  • summary 后再补回关键附件

Open-ClaudeCode 的锚点:

  • 主循环中的 compact 逻辑:query.ts
  • compact 服务:compact.ts

5.7 Java 工程师要抓住的抽象

memory 不等于向量库。

更准确的理解是:

  • 会话记忆 = messages + transcript
  • 工作记忆 = runtime state + cache
  • 长期记忆 = 规则文件 + memory 文件
  • 压缩记忆 = summary + compact boundary

6. Hook / Permission / Safety

6.1 Hook / 事件拦截机制

Hook 解决的问题是:

不要把日志、审计、策略、额外上下文注入、外部联动都写死在主循环里。

它本质上是 Harness 的拦截器总线。

Open-ClaudeCode 的锚点:

  • Hook 引擎:hooks.ts
  • Tool Hook 封装:toolHooks.ts

典型事件包括:

  • UserPromptSubmit
  • PreToolUse
  • PostToolUse
  • PermissionDenied
  • Stop
  • SubagentStart

Java 类比:

  • Servlet Filter
  • HandlerInterceptor
  • AOP Around Advice
  • Domain Event

6.2 Tool 前后的权限控制

在真正的 Agent 里,Tool 不是"模型要调用就调用"。

典型流程是:

text 复制代码
解析输入
-> Schema 校验
-> 业务校验
-> PreTool Hook
-> 权限规则判定
-> 如有必要要求确认
-> 执行 Tool
-> PostTool Hook

Open-ClaudeCode 的锚点:

  • 权限引擎:permissions.ts
  • 执行与权限结合:toolExecution.ts

6.3 危险命令 / 高风险操作确认

只要 Tool 具备下面任意一种能力,就需要确认机制:

  • 写文件
  • 执行命令
  • 访问网络
  • 调起子 Agent

成熟系统通常支持:

  • ALLOW
  • DENY
  • ASK

这本质上是 step-up authorization。

Open-ClaudeCode 的锚点:

  • 危险权限识别:permissionSetup.ts

6.4 这一层为什么是核心

很多人把安全机制当"产品增强项",这是错误的。

对 coding agent 来说:

  • Tool Runtime
  • Permission
  • Dangerous Action Confirmation

这三者和主循环一样,都是 Harness 内核的一部分。


7. Verification:测试 / 修复 / 总结

7.1 为什么 Verification 是 Agent 架构的一部分

一个真正有工程价值的 coding agent,不应该停在"我改完了"。

更合理的闭环是:

  1. 读取和理解
  2. 修改
  3. 生成 diff
  4. 运行验证
  5. 失败则修复
  6. 最后总结结果

这不是"体验优化",而是工程闭环。

7.2 Open-ClaudeCode 中可以看到的信号

  • 编辑类 Tool:FileEditTool.ts
  • diff 能力:gitDiff.ts
  • verify 技能:verify.ts

7.3 Java 类比

你可以把 Verification 理解成:

  • 一个轻量 CI Loop
  • Patch + Diff + Validate 的执行链

7.4 关键学习点

对 Java 工程师来说,这一节最重要的思想是:

Agent 不只负责"生成方案",还应该负责"验证结果"。


8. Subagent / MCP / Plugin 的定位

8.1 Subagent

Subagent 的作用是:

  • 任务分解
  • 角色化协作
  • 上下文隔离
  • 权限隔离

Open-ClaudeCode 的锚点:

  • 子 Agent Tool:AgentTool.tsx
  • 子 Agent 运行:runAgent.ts
  • fork 机制:forkedAgent.ts

Java 类比:

  • Child Workflow
  • Composite Command
  • Fork/Join

定位:

  • 很有价值
  • 但不是 MVP 必需项

8.2 MCP

MCP 的作用是统一接外部能力,包括:

  • 外部 Tool
  • 外部 Resource
  • 外部 Prompt / Command

Open-ClaudeCode 的锚点:

  • MCP 客户端:client.ts
  • MCP Tool 封装:MCPTool.ts

Java 类比:

  • Connector SDK
  • Integration Gateway
  • Adapter Bus

定位:

  • 高价值扩展项
  • 不是 Harness Kernel 必需项

8.3 Plugin

Plugin 的作用是:

  • 包装扩展能力
  • 支持安装、启停、加载
  • 不改主仓库也能增量扩展

Open-ClaudeCode 的锚点:

  • 插件加载器:pluginLoader.ts
  • 插件 Agent 加载:loadPluginAgents.ts

Java 类比:

  • Spring Boot Auto Configuration
  • SPI / ServiceLoader
  • Plugin Container

定位:

  • 平台化增强项
  • Demo 完全可以先不做

8.4 一句话归类

text 复制代码
Kernel:
Execution Loop / Tool Runtime / Memory / Permission / Safety

Advanced:
Verification / Subagent / MCP

Platform:
Plugin / Remote Bridge / Marketplace Style Extension

9. Java 工程师应该如何迁移思维

9.1 从"请求响应"迁移到"状态循环"

传统 Spring Boot 思维:

  • 一次请求
  • 一次服务调用链
  • 一次响应

Agent 思维:

  • 一次用户意图
  • 多轮推理和动作
  • 模型输出驱动下一步
  • 状态跨轮保留

9.2 从"方法调用"迁移到"受控命令执行"

不要把 Tool 看成工具类方法。

更准确的理解是:

  • Tool 是命令
  • Tool Runtime 是命令执行总线
  • Permission 是命令闸门
  • Hook 是命令拦截器

9.3 从"模板 Prompt"迁移到"动态上下文组装"

不要把 Prompt 看成一个静态大字符串。

真正工程化的 Prompt 由多部分组成:

  • system prompt
  • 项目规则
  • 当前任务信息
  • 历史消息
  • tool result
  • memory / summary / attachments

9.4 从"日志"迁移到"可追踪的推理执行"

一个成熟 Agent 不能只记接口日志。

它至少要能追踪:

  • 当前 turn
  • 当前消息集
  • Tool 调用
  • 权限决策
  • verify 结果
  • summary / compact 行为

9.5 从"业务服务"迁移到"Runtime 内核"

不要先想:

  • AgentService.answer()

要先想:

  • HarnessLoopEngine
  • ToolRuntime
  • SessionState
  • PolicyEngine

10. 一条循序渐进的学习路线

阶段一:先吃透内核

先学这些:

  • 什么是 Harness
  • Execution Loop
  • Prompt / Context Assembly
  • Tool Runtime
  • Session State

目标:

你能完整解释"一条用户输入如何经过多轮 model-tool-model 执行直到结束"。

阶段二:再学治理能力

继续学:

  • Hook
  • Permission
  • Dangerous Action Confirmation
  • Audit

目标:

你能解释"为什么 Agent 不是 LLM + Tools 的简单拼接"。

阶段三:再学记忆和长任务稳定性

继续学:

  • transcript
  • compact
  • summary
  • 工作记忆
  • 项目规则注入

目标:

你能解释"为什么 Agent 不会每轮失忆,也不会长上下文崩掉"。

阶段四:最后学扩展能力

继续学:

  • Verification 闭环
  • Subagent
  • MCP
  • Plugin
  • Remote Bridge

目标:

你能清楚区分:

  • 什么是 Harness 必需项
  • 什么是高级增强项
  • 什么是平台产品能力

11. 如何基于 LangChain4j 自己实现一个最小 Harness

11.1 总原则

可以用 LangChain4j 做模型集成,但不要把 LangChain4j 当成完整架构。

特别不要让 @Tool 注解方法直接成为你的全部 Tool Runtime。

更稳妥的做法是:

  • LangChain4j 只负责模型调用
  • Harness 自己实现 Loop、Tool Runtime、State、Permission、Memory

11.2 建议的最小类设计

java 复制代码
public class SessionState {
    String sessionId;
    List<Message> messages;
    int turnCount;
    WorkingMemory workingMemory;
}

public class WorkingMemory {
    Set<String> readFiles;
    Set<String> invokedSkills;
    Map<String, Object> attributes;
}

public interface PromptAssembler {
    PromptEnvelope build(SessionState session, UserInput input);
    PromptEnvelope rebuildFromSession(SessionState session);
}

public interface LlmGateway {
    LlmResponse generate(PromptEnvelope prompt, List<ToolSpec> visibleTools);
}

public interface ToolRuntime {
    List<ToolResultMessage> execute(List<ToolCall> calls, ToolContext context);
    ToolExecutionResult executeOne(ToolCall call, ToolContext context);
}

public interface PermissionService {
    PermissionDecision decide(ToolCall call, ToolContext context);
}

public interface HookChain {
    void beforeTool(ToolCall call, ToolContext context);
    void afterTool(ToolCall call, ToolExecutionResult result, ToolContext context);
}

public interface TranscriptStore {
    void append(String sessionId, Message message);
    List<Message> load(String sessionId);
}

11.3 最小主循环伪代码

java 复制代码
public class MinimalHarness {

    public AssistantMessage run(SessionState session, UserInput input) {
        PromptEnvelope prompt = promptAssembler.build(session, input);

        while (true) {
            LlmResponse response =
                llmGateway.generate(prompt, toolRegistry.visibleTools());

            AssistantMessage assistant = response.toAssistantMessage();
            session.messages.add(assistant);
            transcriptStore.append(session.sessionId, assistant);

            List<ToolCall> calls = assistant.extractToolCalls();
            if (calls.isEmpty()) {
                return assistant;
            }

            List<ToolResultMessage> toolResults = new ArrayList<>();

            for (ToolCall call : calls) {
                hookChain.beforeTool(call, toolContext);

                PermissionDecision decision =
                    permissionService.decide(call, toolContext);

                if (!decision.isAllowed()) {
                    toolResults.add(ToolResultMessage.denied(call));
                    continue;
                }

                ToolExecutionResult result =
                    toolRuntime.executeOne(call, toolContext);

                hookChain.afterTool(call, result, toolContext);
                toolResults.add(result.toToolResultMessage(call));
            }

            session.messages.addAll(toolResults);
            for (ToolResultMessage msg : toolResults) {
                transcriptStore.append(session.sessionId, msg);
            }

            prompt = promptAssembler.rebuildFromSession(session);
        }
    }
}

11.4 MVP 必须有的东西

  • 持久化 messages
  • 一个最小 TranscriptStore
  • 统一 Tool 抽象
  • ALLOW / ASK / DENY 三态权限
  • 至少一个 beforeTool / afterTool 拦截点
  • 基本 timeout / interrupt

11.5 MVP 可以先不做的东西

  • Plugin
  • MCP
  • Subagent
  • 远程桥接
  • 复杂 summary / compact
  • 很细的 Hook 生命周期

11.6 推荐最小范围

第一版只做这些就够了:

  • 一个 chat 接口
  • 一个 HarnessLoopEngine
  • 一个 SessionStateStore
  • 四个 Tool:Read / Grep / Edit / Bash
  • 一个 PermissionService
  • 一个 TranscriptStore
  • 一个简单的 summary 或 compact 策略

这已经足够让你真正掌握 Harness,而不是只停留在"会调模型"。


12. 最后总结

通过 Claude Code 这类项目,Java 工程师最应该学到的,不是某个 Prompt 写法,而是下面这些工程抽象:

  • Harness 是 Runtime 内核,不是 UI 功能
  • Execution Loop 是状态机,不是一次 RPC
  • Tool Runtime 是受控命令执行框架,不是工具方法集合
  • Memory 是分层状态保存协议,不等于向量库
  • Hook / Permission / Safety 是内核必需项,不是后补的装饰
  • Verification 让 Agent 从"会说"变成"会做并会验证"
  • Subagent / MCP / Plugin 是高级扩展,不是第一天就必须做

如果你把这些抽象吃透,再去看具体模型、具体框架、具体协议,理解成本会低很多。


13. 建议配合阅读的源码锚点

如果你想把这份学习文档和 ClaudeCode 的源码对应起来,可以按下面这些锚点看:

  • Harness 主循环:query.ts, QueryEngine.ts
  • Prompt / Context:queryContext.ts, context.ts, systemPrompt.ts
  • Tool Runtime:Tool.ts, tools.ts, toolExecution.ts, toolOrchestration.ts
  • State / Memory:AppStateStore.ts, sessionStorage.ts, memdir.ts, compact.ts
  • Hook / Permission / Safety:hooks.ts, permissions.ts, permissionSetup.ts
  • Verification:FileEditTool.ts, gitDiff.ts, verify.ts
  • 高级扩展:AgentTool.tsx, runAgent.ts, client.ts, pluginLoader.ts, replBridge.ts
相关推荐
IGAn CTOU2 小时前
PHP使用Redis实战实录2:Redis扩展方法和PHP连接Redis的多种方案
开发语言·redis·php
爱敲代码的小鱼2 小时前
springboot(2)从基础到项目创建:
java·spring boot·spring
环黄金线HHJX.2 小时前
TSE框架配置与部署详解
开发语言·python
Vfw3VsDKo2 小时前
Maui 实践:Go 接口以类型之名,给 runtime 传递方法参数
开发语言·后端·golang
Pyeako3 小时前
PyQt5 + PaddleOCR实战:打造桌面级实时文字识别工具
开发语言·人工智能·python·qt·paddleocr·pyqt5
迈巴赫车主3 小时前
蓝桥杯19724食堂
java·数据结构·算法·职场和发展·蓝桥杯
i220818 Faiz Ul4 小时前
动漫商城|基于springboot + vue动漫商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·动漫商城系统
白藏y4 小时前
【C++】muduo接口补充
开发语言·c++·muduo
海兰4 小时前
【实战】MCP 服务在 Nacos 中注册状态分析与优化
android·java·github·银行系统·银行ai