AgentScope Java 2.0 源码学习手册 v2

这版文档面向“快速学会 + 源码看懂 + Java Agent 自研可借鉴”。核心结论:AgentScope Java 不是简单 ReAct Demo,而是一套 Java 生产级 Agent Runtime 与 Harness 工程套件。

JDK 17+Reactor 非阻塞ReActAgentHarnessAgentPermission / HITLDistributedStore
01

定位与学习目标

AgentScope Java 2.0 的定位是:面向分布式、企业级、长时间运行、安全可控 Agent 的生产级 Java 框架。README 明确列出 Event System、Permission System、Middleware、Workspace & Sandbox、Multi-Agent Orchestration、Distributed Deployment 等能力。

RuntimeAgentBase / ReActAgent 负责生命周期、推理-行动循环、事件流、上下文与状态。
HarnessHarnessAgent 在核心 Agent 外增加 workspace、sandbox、memory、skill、subagent、plan mode。
ProductionDistributedStore、AgentStateStore、SandboxSnapshot、ExecutionGuard 用于多副本生产环境。
学习目标:不是只会调用 API,而是能讲清楚 AgentScope Java 的底层执行链路、扩展点、状态恢复边界和生产部署检查项。
源码依据:README、ReActAgent、AgentBase、HarnessAgent、Going to Production。
02

30 分钟快速上手路径

第一次学习不要直接陷入全部源码。先按这条路径建立“能跑、能看、能断点”的感觉。

0-5 分钟:看 README 定位
确认 Maven 依赖、JDK 17+、HarnessAgent Hello World、模型 provider extension。
5-10 分钟:跑 HarnessAgent
只用模型,不加工具,观察 call() 与 streamEvents() 的返回差异。
10-15 分钟:注册一个 @Tool
让模型生成 ToolUseBlock,再由 Toolkit.callTools() 执行。
15-20 分钟:打开 streamEvents
观察 MODEL_CALL、TEXT_BLOCK、TOOL_CALL、TOOL_RESULT、AGENT_RESULT 等事件。
20-25 分钟:加 Permission ASK
模拟敏感工具需要用户确认,理解 HITL 暂停与下一次 call 恢复。
25-30 分钟:加 AgentStateStore
传入 RuntimeContext.userId/sessionId,观察状态如何按会话隔离。
03

仓库分层架构

层模块职责学习重点
核心运行时agentscope-coreAgentBase、ReActAgent、Message、Event、Model、Tool、Permission、StateStore。理解 Agent 如何运行、如何调用模型和工具。
工程增强层agentscope-harnessWorkspace、Sandbox、Memory、Skill、Subagent、Plan Mode、MCP 编排。理解生产 Agent 需要哪些工程能力。
扩展层agentscope-extensions模型 provider、Redis/MySQL/OSS、A2A/AG-UI、MCP、观测等。理解如何接入外部基础设施。
控制面agentscope-serviceAgent 注册、发现、观察、编排、控制台能力。理解多 Agent 平台化方向。
一句话:agentscope-core 解决“Agent 怎么跑”,agentscope-harness 解决“Agent 怎么上线”。
04

最小运行示例:从 HarnessAgent 开始

生产学习优先从 HarnessAgent 开始,因为它是用户侧工程入口;只做底层实验时再直接使用 ReActAgent。

Maven 依赖

<dependency>
  <groupId>io.agentscope</groupId>
  <artifactId>agentscope-harness</artifactId>
  <version>2.0.1</version>
</dependency>

<dependency>
  <groupId>io.agentscope</groupId>
  <artifactId>agentscope-extensions-model-dashscope</artifactId>
  <version>2.0.1</version>
</dependency>

最小 HarnessAgent

HarnessAgent agent = HarnessAgent.builder()
    .name("assistant")
    .sysPrompt("You are a helpful AI assistant.")
    .model("dashscope:qwen-plus")
    .workspace(Paths.get(".agentscope/workspace"))
    .build();

RuntimeContext ctx = RuntimeContext.builder()
    .userId("alice")
    .sessionId("demo-session")
    .build();

agent.call(new UserMessage("Hello!"), ctx).block();
学习建议:每次调用都显式传 RuntimeContext.userId 和 sessionId,这样能更早理解状态隔离、恢复和同 session 串行化。
05

AgentBase:统一生命周期与同 session 串行化

AgentBase 是所有 Agent 的生命周期底座。它不负责具体 ReAct 逻辑,而负责执行框架共性问题:Reactor Context、call scope、pre/post hook、tracing、shutdown、错误处理、同 key 串行化。

call(msgs, RuntimeContext) ↓ callInternal(...) ↓ runLifecycle(...) ↓ 读取 Reactor Context 中的 RuntimeContext ↓ register graceful shutdown request ↓ 按 callSerializationKey 串行化同 session 调用 ↓ beforeAgentExecution() 生成 per-call scope ↓ preCall → doCall → postCall ↓ 错误处理 / interrupt recovery / releaseExecution
机制解决的问题
CALL_SCOPE_KEY把每次调用的 per-call scope 放入 Reactor Context,避免并发调用共享可变状态。
RUNTIME_CONTEXT_KEY让 userId/sessionId、工具上下文、sandbox 上下文随订阅流动。
callGates同一个 key 的调用 FIFO 串行,不同 key 可并发。
handleInterrupt()为中断、shutdown、部分推理结果恢复提供统一扩展点。
06

ReActAgent:推理 Reasoning + 行动 Acting 主循环

ReActAgent 是 AgentScope Java 的核心 Agent 实现。它在 reasoning 与 acting 之间循环:模型先思考并生成文本或工具调用;如果有工具调用,则进入 acting 执行工具;工具结果写回上下文,再继续 reasoning。

coreAgent() ↓ executeIteration(0) ↓ reasoning(iter) ├─ firePreReasoning ├─ build modelInput + toolSchemas ├─ model.stream(messages, tools, options) ├─ ReasoningContext 累积 chunk → Msg / ToolUseBlock └─ runPostReasoningPipeline ├─ finished → return Msg └─ not finished → acting(iter) ├─ extractPendingToolCalls ├─ evaluatePermissions ├─ Toolkit.callTools ├─ ToolResultBlock → ToolResultMessageBuilder └─ context.add(tool result) → executeIteration(iter + 1)
核心理解:LLM 并不直接执行工具。LLM 只输出 ToolUseBlock;真正执行由 acting() 阶段通过 Toolkit 完成。
07

模型调用抽象:Model.stream()

模型层是一个很薄的抽象:Model.stream(List<Msg>, List<ToolSchema>, GenerateOptions) 返回 Flux<ChatResponse>。这让 OpenAI、DashScope、Anthropic、Gemini、Ollama 等 provider 可以被统一接入。

public interface Model {
    Flux<ChatResponse> stream(
        List<Msg> messages,
        List<ToolSchema> tools,
        GenerateOptions options
    );

    String getModelName();

    default boolean supportsNativeStructuredOutput() { return false; }
    default boolean supportsNativeStructuredOutputWithTools() {
        return supportsNativeStructuredOutput();
    }
}

这个接口的关键不是复杂,而是边界清楚:Agent Runtime 负责流程、状态、工具和事件;模型实现负责把 AgentScope 的消息和工具 schema 转成具体 provider 请求。

08

消息与事件流:为什么适合前端实时渲染

AgentScope Java 2.0 使用细粒度 AgentEvent,可以把模型流式输出、工具调用、工具结果、用户确认、外部执行等过程直接推给前端。

事件类别作用前端用途
AGENT_START / AGENT_END一次 Agent 调用的开始和结束。控制 loading、耗时统计。
MODEL_CALL_START / END模型调用边界。显示“正在思考”。
TEXT_BLOCK_DELTA文本增量。打字机效果。
TOOL_CALL_START / DELTA / END模型发起工具调用。展示工具名称和参数。
TOOL_RESULT_*工具执行结果或流式结果。展示搜索、文件、代码执行进度。
REQUIRE_USER_CONFIRM权限 ASK。弹出人工确认。
REQUIRE_EXTERNAL_EXECUTION外部工具需要用户/系统执行。集成人工或外部系统闭环。
09

@Tool 最小示例:从方法变成工具

Toolkit 可以扫描带 @Tool 注解的方法,生成工具 schema,并在模型返回工具调用时反射执行。

public class WeatherTools {
    @Tool(name = "get_weather", description = "Get weather by city name")
    public String getWeather(String city) {
        return city + " today is sunny";
    }
}

Toolkit toolkit = new Toolkit();
toolkit.registerTool(new WeatherTools());

ReActAgent agent = ReActAgent.builder()
    .name("assistant")
    .model(model)
    .toolkit(toolkit)
    .maxIters(8)
    .build();
@Tool 方法 ↓ Toolkit.registerTool(object) ↓ ReflectiveFunctionTool ↓ ToolSchemaGenerator 生成 JSON Schema ↓ model.stream(..., tools, ...) ↓ 模型输出 ToolUseBlock ↓ Toolkit.callTools → 反射执行方法 ↓ ToolResultBlock 写回上下文
10

Toolkit:工具注册、工具组、MCP 与执行 facade

Toolkit 是工具系统 facade,内部委托多个 manager:ToolRegistry、ToolGroupManager、ToolSchemaProvider、McpClientManager、MetaToolFactory、ToolExecutor。

组件职责
ToolRegistry保存工具名到工具对象的映射。
ToolGroupManager管理工具组、动态启用/禁用工具组。
ToolSchemaProvider按 activeGroups 过滤工具 schema。
McpClientManager注册外部 MCP 工具。
ToolExecutor执行工具,支持并行/串行、超时、重试、校验和结果转换。
自研借鉴:工具系统不要写成一个大类。更合理的结构是“注册表 + schema 生成 + 分组管理 + 执行器 + 权限裁决 + 结果转换”。
11

streamEvents:观察 Agent 的最佳入口

call() 只拿最终结果;streamEvents() 可以观察完整运行过程。学习源码时,优先用 streamEvents()。

agent.streamEvents(new UserMessage("帮我查天气"), ctx)
    .doOnNext(event -> {
        switch (event.getType()) {
            case MODEL_CALL_START -> System.out.println("模型开始调用");
            case TEXT_BLOCK_DELTA -> System.out.print(event);
            case TOOL_CALL_START -> System.out.println("工具开始调用");
            case TOOL_RESULT_END -> System.out.println("工具执行完成");
            case REQUIRE_USER_CONFIRM -> System.out.println("需要用户确认");
            default -> {}
        }
    })
    .blockLast();

如果你要做 Web UI,建议直接围绕 AgentEvent 设计前端协议,而不是只返回最终文本。

12

PermissionEngine:权限决策树

PermissionEngine 是工具安全控制的核心。源码注释明确给出权限评估顺序:deny 最高,其次 ask,再 tool-specific checks,再 allow,再 BYPASS,最后 default ASK 或 DONT_ASK 下转 DENY。

Tool call input ↓ 1. 命中 deny rule ? └─ 是 → DENY ↓ 2. 命中 ask rule ? └─ 是 → ASK / REQUIRE_USER_CONFIRM ↓ 3. tool-specific check ├─ DENY → DENY ├─ ASK → ASK └─ ALLOW/PASSTHROUGH → 继续 ↓ 4. 命中 allow rule ? └─ 是 → ALLOW ↓ 5. PermissionMode.BYPASS ? └─ 是 → ALLOW ↓ 6. 默认 ASK;DONT_ASK 模式下转 DENY
安全要点:不要只做 allow list。生产工具系统需要 deny 优先、敏感工具 ASK、工具自身安全检查、审计日志和执行幂等。
13

HITL:暂停、确认与恢复

HITL 的本质是:模型已经提出工具调用,但运行时不立即执行,而是把工具调用标为 ASKING,返回确认事件;用户确认后,下一次 agent.call() 带上 ConfirmResult,Agent 再继续执行。

模型输出敏感 ToolUseBlock ↓ acting() → evaluatePermissions() ↓ PermissionBehavior.ASK ↓ ToolUseBlock.state = ASKING ↓ 发布 REQUIRE_USER_CONFIRM ↓ 本轮返回 GenerateReason.PERMISSION_ASKING ↓ 用户确认 / 修改参数 / 拒绝 ↓ 下一次 call 携带 ConfirmResult ↓ validateAndAcceptConfirmResults() ↓ ASKING → ALLOWED 或写入 DENIED ToolResultBlock ↓ resumeAgent() → acting()

这套设计让用户确认成为 Agent Runtime 的一等公民,而不是业务层临时加弹窗。

14

AgentStateStore:状态持久化与 CAS

AgentStateStore 按 (userId, sessionId) 保存状态。状态项包括会话上下文、权限状态、工具状态、Plan Mode 状态等。版本化 backend 可以通过 getVersioned() 和 saveIfVersion() 做乐观并发控制。

接口意义
save(userId, sessionId, key, value)保存单个状态对象。
get(userId, sessionId, key, type)读取状态对象。
getVersioned(...)读取状态及其版本。
saveIfVersion(..., expectedVersion)CAS 写入;版本冲突时不覆盖。
listSessionIds(userId)列出用户下的 session。
关键理解:AgentScope Java 的恢复粒度主要是 session state,而不是 LangGraph 那种图节点/通道级 checkpoint。
15

恢复边界与外部副作用幂等

状态恢复最容易被误解。AgentStateStore 可以恢复会话上下文和工具状态,但不能天然保证外部副作用 exactly-once。

必须记住:AgentStateStore 能恢复 Agent 状态;支付、发邮件、删文件、写订单、调用第三方 API 等外部副作用,必须由业务系统保证幂等。
场景StateStore 能解决还需要业务层解决
服务重启后继续对话恢复 context、permission、tool state。确认前一次外部操作是否已经完成。
同 session 多副本并发CAS 检测版本冲突。业务侧 turn gate、幂等 key、冲突策略。
工具执行中断可合成 error tool result 或等待外部结果。外部系统需要可查询、可重试、可补偿。
支付/订单类工具保存工具调用上下文。idempotency_key、订单流水表、结果回查、审计日志。
// 自研工具幂等建议:每次工具调用生成稳定业务幂等键
String idempotencyKey = sessionId + ":" + toolCallId + ":" + toolName;

// 业务数据库先写入 pending,再执行外部副作用
// 若重试发现 idempotencyKey 已成功,直接返回历史结果
// 若 pending 超时,走查询/补偿逻辑,而不是盲目再次执行
16

结构化输出:Native 与 generate_response fallback

结构化输出有两条路径:模型支持原生 response_format/json_schema 时走 Native;否则注入一个 per-call 合成工具 generate_response,让模型通过工具调用返回结构化结果。

路径触发条件优点注意点
Native structured output模型支持 native structured output,且可与 tools 共存。请求更短,模型直接输出 JSON。有些 provider 在 tools + response_format 下行为不稳定。
Fallback synthetic tool模型不支持 native 或 native 调用失败。与 ReAct 工具循环兼容,适配范围广。需要注入 generate_response 工具,并在工具结果中提取结构化数据。
doStructuredCall() ├─ supportsNativeStructuredOutput? → doNativeStructuredCall() │ └─ response_format=json_schema └─ fallback → doFallbackStructuredCall() ├─ createStructuredOutputTool("generate_response") ├─ 添加到本次 call 的 tools ├─ 模型调用 generate_response └─ extractStructuredResult()
17

HarnessAgent:生产工程层

HarnessAgent 是用户侧生产入口。它包装内部 ReActAgent,并在上层增加 workspace、filesystem、sandbox、subagent、skill、plan-mode、MCP orchestration、context compaction 等能力。

能力解决的问题
Workspace context加载 AGENTS.md、MEMORY.md、KNOWLEDGE.md 等工作区上下文。
Filesystem抽象 local、remote、sandbox 文件系统。
Sandbox隔离执行 shell、代码、工具,支持快照恢复。
Memory记忆读写、flush、检索、上下文压缩。
Skill技能仓库、动态技能加载、自学习与审核。
Subagent通过 task / task_output 或 agent_spawn / agent_send 做多 Agent 协作。
Plan Mode长任务先规划后执行,降低盲目工具调用风险。
18

Workspace 与 Sandbox:Agent 工程化的两条腿

Workspace

提供 Agent 的长期工作目录、记忆文件、技能文件、项目上下文。它解决“Agent 怎么记住和组织自己的工作资料”。

Sandbox

提供隔离执行环境,可以是本地、Docker、Kubernetes 或云沙箱。它解决“Agent 执行工具和代码时如何安全隔离”。

模式适合场景风险
Local filesystem本地开发、可信工具。权限边界弱,不适合不可信代码。
Remote filesystem多副本共享长期记忆和文件。需要 BaseStore、命名空间隔离。
Sandbox filesystem执行代码、shell、用户上传项目。需要 snapshot、execution guard、资源限制。
19

分布式生产部署检查清单

官方生产文档强调:从单机到多副本,核心变化是多个 replica 必须共享 session、隔离用户、沙箱执行不可信代码,并能在 pod 重启后恢复会话。

检查项开发环境生产环境
AgentStateStoreInMemory / JsonFileRedis / MySQL / PostgreSQL / OSS 等持久化后端。
RuntimeContext可省略 userId必须传 userId + sessionId,并包含 tenant/agent 维度。
FilesystemLocalRemote 或 Sandbox,并设置 IsolationScope。
Sandbox SnapshotNoop / LocalRedis / OSS / JDBC snapshot,用于跨节点恢复工作区。
Sandbox ExecutionGuard通常不需要Redis/MySQL 锁,避免多副本同时写同一 sandbox。
Skill Repositoryworkspace/skillsGit / MySQL / Nacos 等集中管理。
Observability日志Tracing、Event Stream、Audit、指标。
外部副作用可手工检查幂等键、审计表、结果回查、补偿任务。
DistributedStore store = RedisDistributedStore.fromJedis(jedis);

HarnessAgent agent = HarnessAgent.builder()
    .name("prod-agent")
    .model(model)
    .distributedStore(store)
    .filesystem(new RemoteFilesystemSpec()
        .isolationScope(IsolationScope.USER))
    .build();
20

与 LangGraph 的本质区别

维度AgentScope JavaLangGraph
核心抽象Java Agent Runtime + Harness 工程套件。有状态图执行引擎 / durable state machine。
执行模型ReAct reasoning/acting loop 为主。Graph node/edge/channel/checkpoint 为主。
恢复粒度Session state / AgentStateStore。图执行 checkpoint / pending writes。
工程能力Workspace、Sandbox、Permission、Skill、Subagent、DistributedStore。Graph orchestration、interrupt/resume、state reducers、LangSmith/Platform。
适合Java 技术栈、生产 Agent 服务、自研工具系统。复杂工作流、多节点 Agent 编排、Python/JS 生态。
一句话:LangGraph 更像 Agent 工作流执行引擎;AgentScope Java 更像 Java 生产 Agent 框架与运行时工程套件。
21

源码阅读路线

  1. README.md:先确认项目定位、模块、快速开始、2.0 能力。
  2. agentscope-core/.../AgentBase.java:理解生命周期、Reactor Context、同 key 串行化。
  3. agentscope-core/.../ReActAgent.java:理解 reasoning、acting、structured output、HITL、state save。
  4. agentscope-core/.../model/Model.java:理解模型 provider 如何被抽象。
  5. agentscope-core/.../tool/Toolkit.java:理解工具注册、工具组、MCP、ToolExecutor。
  6. agentscope-core/.../permission/PermissionEngine.java:理解权限优先级。
  7. agentscope-core/.../state/AgentStateStore.java:理解状态保存、恢复和 CAS。
  8. agentscope-harness/.../HarnessAgent.java:理解 Workspace、Sandbox、Memory、Skill、Subagent 的工程入口。
  9. docs/v2/en/docs/others/going-to-production.md:理解生产部署替换哪些组件。
22

断点调试路线

断点位置观察变量你要理解的问题
AgentBase.runLifecycle()RuntimeContext、gateKey、requestId一次调用如何进入统一生命周期。
AgentBase.serializeOnKey()callGates、当前 key为什么同 session 串行,不同 session 并行。
ReActAgent.activateSlotForContext()userId、sessionId、slotKey、AgentState状态如何按用户和会话隔离。
CallExecution.reasoning()modelInput、tools、GenerateOptions发给模型的上下文和工具 schema 是什么。
reasoningStream()AgentEvent、ReasoningContext模型流如何被组装成消息和事件。
CallExecution.acting()pendingToolCalls、PermissionGate工具调用如何进入执行或 HITL。
Toolkit.callTools()effectiveConfig、parallel、ToolResultBlock工具如何执行、如何处理超时和错误。
persistAgentStateCas()expectedVersion、conflictPolicy多副本状态写入冲突如何处理。
23

Java 自研 Agent Runtime 可借鉴点

1. RuntimeContext 贯穿全链路

userId、sessionId、工具上下文、sandbox 上下文必须贯穿模型、工具、状态和中间件。

2. AgentBase / ReActAgent 分离

生命周期底座和具体 Agent 策略分离,避免 ReActAgent 变成所有职责的大泥球。

3. 工具系统 facade 化

工具注册、schema、执行、权限、MCP、分组管理分模块处理。

4. 事件优先

前端、审计、观测、HITL 都围绕事件流设计,而不是只返回最终字符串。

5. 状态存储要有 CAS

多副本生产环境中,状态写入必须考虑版本冲突和合并策略。

6. 外部副作用业务幂等

Runtime 可以保存状态,但无法替代订单、支付、邮件等业务系统的幂等表。

24

面试讲解话术

30 秒版本:

AgentScope Java 2.0 是一个 Java 生产级 Agent Runtime。底层由 AgentBase 统一生命周期,ReActAgent 实现 reasoning/acting 循环,Toolkit 负责工具注册和执行,PermissionEngine 控制工具安全和 HITL,AgentStateStore 按 userId/sessionId 持久化会话状态。HarnessAgent 在核心 Agent 外再加 Workspace、Sandbox、Memory、Skill、Subagent 和 DistributedStore,解决生产部署问题。

2 分钟版本:

我会从两层解释:第一层是 agentscope-core,它解决 Agent 怎么运行,包括生命周期、模型调用、消息事件、工具调用、权限和状态;第二层是 agentscope-harness,它解决 Agent 怎么上线,包括工作区、沙箱、长期记忆、技能、多 Agent、上下文压缩和分布式存储。它和 LangGraph 的区别是:LangGraph 偏图状态机和 checkpoint 执行引擎,AgentScope Java 偏 Java 技术栈的生产 Agent Runtime 和工程套件。

25

架构师自测题

  1. AgentScope Java 为什么不能只理解成 ReAct Demo?
  2. AgentBase.runLifecycle() 解决了哪些通用运行时问题?
  3. 为什么 RuntimeContext 要通过 Reactor Context 传递?
  4. 同一个 userId/sessionId 的调用为什么需要串行化?
  5. 模型输出的 ToolUseBlock 如何变成真正工具执行?
  6. Toolkit 为什么设计成 facade,而不是一个简单工具列表?
  7. PermissionEngine 的优先级是什么?为什么 deny 要最高?
  8. HITL 暂停后,下一次 call() 如何恢复?
  9. AgentStateStore.saveIfVersion() 解决什么问题?
  10. 为什么 AgentStateStore 不能保证支付、发邮件等外部副作用 exactly-once?
  11. 结构化输出为什么需要 Native 与 synthetic tool 两条路径?
  12. HarnessAgent 相比裸 ReActAgent 增加了哪些生产能力?
  13. 分布式部署中为什么需要 SandboxExecutionGuard?
  14. 如果用 Java 自研 Agent Runtime,你会复用哪些设计?
完成标准:能把一次用户请求从 HarnessAgent.call() 讲到 AgentBase.runLifecycle(),再讲到 ReActAgent.reasoning()、Toolkit.callTools()、AgentStateStore.save(),并能说明生产环境的状态恢复边界和幂等责任。
← 返回内容索引