AgentScope Java 2.0 源码学习手册 v2
这版文档面向“快速学会 + 源码看懂 + Java Agent 自研可借鉴”。核心结论:AgentScope Java 不是简单 ReAct Demo,而是一套 Java 生产级 Agent Runtime 与 Harness 工程套件。
定位与学习目标
AgentScope Java 2.0 的定位是:面向分布式、企业级、长时间运行、安全可控 Agent 的生产级 Java 框架。README 明确列出 Event System、Permission System、Middleware、Workspace & Sandbox、Multi-Agent Orchestration、Distributed Deployment 等能力。
30 分钟快速上手路径
第一次学习不要直接陷入全部源码。先按这条路径建立“能跑、能看、能断点”的感觉。
确认 Maven 依赖、JDK 17+、HarnessAgent Hello World、模型 provider extension。
只用模型,不加工具,观察
call() 与 streamEvents() 的返回差异。让模型生成
ToolUseBlock,再由 Toolkit.callTools() 执行。观察 MODEL_CALL、TEXT_BLOCK、TOOL_CALL、TOOL_RESULT、AGENT_RESULT 等事件。
模拟敏感工具需要用户确认,理解 HITL 暂停与下一次 call 恢复。
传入
RuntimeContext.userId/sessionId,观察状态如何按会话隔离。仓库分层架构
| 层 | 模块 | 职责 | 学习重点 |
|---|---|---|---|
| 核心运行时 | agentscope-core | AgentBase、ReActAgent、Message、Event、Model、Tool、Permission、StateStore。 | 理解 Agent 如何运行、如何调用模型和工具。 |
| 工程增强层 | agentscope-harness | Workspace、Sandbox、Memory、Skill、Subagent、Plan Mode、MCP 编排。 | 理解生产 Agent 需要哪些工程能力。 |
| 扩展层 | agentscope-extensions | 模型 provider、Redis/MySQL/OSS、A2A/AG-UI、MCP、观测等。 | 理解如何接入外部基础设施。 |
| 控制面 | agentscope-service | Agent 注册、发现、观察、编排、控制台能力。 | 理解多 Agent 平台化方向。 |
agentscope-core 解决“Agent 怎么跑”,agentscope-harness 解决“Agent 怎么上线”。最小运行示例:从 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 串行化。AgentBase:统一生命周期与同 session 串行化
AgentBase 是所有 Agent 的生命周期底座。它不负责具体 ReAct 逻辑,而负责执行框架共性问题:Reactor Context、call scope、pre/post hook、tracing、shutdown、错误处理、同 key 串行化。
| 机制 | 解决的问题 |
|---|---|
CALL_SCOPE_KEY | 把每次调用的 per-call scope 放入 Reactor Context,避免并发调用共享可变状态。 |
RUNTIME_CONTEXT_KEY | 让 userId/sessionId、工具上下文、sandbox 上下文随订阅流动。 |
callGates | 同一个 key 的调用 FIFO 串行,不同 key 可并发。 |
handleInterrupt() | 为中断、shutdown、部分推理结果恢复提供统一扩展点。 |
ReActAgent:推理 Reasoning + 行动 Acting 主循环
ReActAgent 是 AgentScope Java 的核心 Agent 实现。它在 reasoning 与 acting 之间循环:模型先思考并生成文本或工具调用;如果有工具调用,则进入 acting 执行工具;工具结果写回上下文,再继续 reasoning。
ToolUseBlock;真正执行由 acting() 阶段通过 Toolkit 完成。模型调用抽象: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 请求。
消息与事件流:为什么适合前端实时渲染
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 | 外部工具需要用户/系统执行。 | 集成人工或外部系统闭环。 |
@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();
Toolkit:工具注册、工具组、MCP 与执行 facade
Toolkit 是工具系统 facade,内部委托多个 manager:ToolRegistry、ToolGroupManager、ToolSchemaProvider、McpClientManager、MetaToolFactory、ToolExecutor。
| 组件 | 职责 |
|---|---|
ToolRegistry | 保存工具名到工具对象的映射。 |
ToolGroupManager | 管理工具组、动态启用/禁用工具组。 |
ToolSchemaProvider | 按 activeGroups 过滤工具 schema。 |
McpClientManager | 注册外部 MCP 工具。 |
ToolExecutor | 执行工具,支持并行/串行、超时、重试、校验和结果转换。 |
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 设计前端协议,而不是只返回最终文本。
PermissionEngine:权限决策树
PermissionEngine 是工具安全控制的核心。源码注释明确给出权限评估顺序:deny 最高,其次 ask,再 tool-specific checks,再 allow,再 BYPASS,最后 default ASK 或 DONT_ASK 下转 DENY。
HITL:暂停、确认与恢复
HITL 的本质是:模型已经提出工具调用,但运行时不立即执行,而是把工具调用标为 ASKING,返回确认事件;用户确认后,下一次 agent.call() 带上 ConfirmResult,Agent 再继续执行。
这套设计让用户确认成为 Agent Runtime 的一等公民,而不是业务层临时加弹窗。
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。 |
恢复边界与外部副作用幂等
状态恢复最容易被误解。AgentStateStore 可以恢复会话上下文和工具状态,但不能天然保证外部副作用 exactly-once。
| 场景 | StateStore 能解决 | 还需要业务层解决 |
|---|---|---|
| 服务重启后继续对话 | 恢复 context、permission、tool state。 | 确认前一次外部操作是否已经完成。 |
| 同 session 多副本并发 | CAS 检测版本冲突。 | 业务侧 turn gate、幂等 key、冲突策略。 |
| 工具执行中断 | 可合成 error tool result 或等待外部结果。 | 外部系统需要可查询、可重试、可补偿。 |
| 支付/订单类工具 | 保存工具调用上下文。 | idempotency_key、订单流水表、结果回查、审计日志。 |
// 自研工具幂等建议:每次工具调用生成稳定业务幂等键
String idempotencyKey = sessionId + ":" + toolCallId + ":" + toolName;
// 业务数据库先写入 pending,再执行外部副作用
// 若重试发现 idempotencyKey 已成功,直接返回历史结果
// 若 pending 超时,走查询/补偿逻辑,而不是盲目再次执行
结构化输出: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 工具,并在工具结果中提取结构化数据。 |
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 | 长任务先规划后执行,降低盲目工具调用风险。 |
Workspace 与 Sandbox:Agent 工程化的两条腿
Workspace
提供 Agent 的长期工作目录、记忆文件、技能文件、项目上下文。它解决“Agent 怎么记住和组织自己的工作资料”。
Sandbox
提供隔离执行环境,可以是本地、Docker、Kubernetes 或云沙箱。它解决“Agent 执行工具和代码时如何安全隔离”。
| 模式 | 适合场景 | 风险 |
|---|---|---|
| Local filesystem | 本地开发、可信工具。 | 权限边界弱,不适合不可信代码。 |
| Remote filesystem | 多副本共享长期记忆和文件。 | 需要 BaseStore、命名空间隔离。 |
| Sandbox filesystem | 执行代码、shell、用户上传项目。 | 需要 snapshot、execution guard、资源限制。 |
分布式生产部署检查清单
官方生产文档强调:从单机到多副本,核心变化是多个 replica 必须共享 session、隔离用户、沙箱执行不可信代码,并能在 pod 重启后恢复会话。
| 检查项 | 开发环境 | 生产环境 |
|---|---|---|
| AgentStateStore | InMemory / JsonFile | Redis / MySQL / PostgreSQL / OSS 等持久化后端。 |
| RuntimeContext | 可省略 userId | 必须传 userId + sessionId,并包含 tenant/agent 维度。 |
| Filesystem | Local | Remote 或 Sandbox,并设置 IsolationScope。 |
| Sandbox Snapshot | Noop / Local | Redis / OSS / JDBC snapshot,用于跨节点恢复工作区。 |
| Sandbox ExecutionGuard | 通常不需要 | Redis/MySQL 锁,避免多副本同时写同一 sandbox。 |
| Skill Repository | workspace/skills | Git / 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();
与 LangGraph 的本质区别
| 维度 | AgentScope Java | LangGraph |
|---|---|---|
| 核心抽象 | 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 生态。 |
源码阅读路线
README.md:先确认项目定位、模块、快速开始、2.0 能力。agentscope-core/.../AgentBase.java:理解生命周期、Reactor Context、同 key 串行化。agentscope-core/.../ReActAgent.java:理解 reasoning、acting、structured output、HITL、state save。agentscope-core/.../model/Model.java:理解模型 provider 如何被抽象。agentscope-core/.../tool/Toolkit.java:理解工具注册、工具组、MCP、ToolExecutor。agentscope-core/.../permission/PermissionEngine.java:理解权限优先级。agentscope-core/.../state/AgentStateStore.java:理解状态保存、恢复和 CAS。agentscope-harness/.../HarnessAgent.java:理解 Workspace、Sandbox、Memory、Skill、Subagent 的工程入口。docs/v2/en/docs/others/going-to-production.md:理解生产部署替换哪些组件。
断点调试路线
| 断点位置 | 观察变量 | 你要理解的问题 |
|---|---|---|
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 | 多副本状态写入冲突如何处理。 |
Java 自研 Agent Runtime 可借鉴点
1. RuntimeContext 贯穿全链路
userId、sessionId、工具上下文、sandbox 上下文必须贯穿模型、工具、状态和中间件。
2. AgentBase / ReActAgent 分离
生命周期底座和具体 Agent 策略分离,避免 ReActAgent 变成所有职责的大泥球。
3. 工具系统 facade 化
工具注册、schema、执行、权限、MCP、分组管理分模块处理。
4. 事件优先
前端、审计、观测、HITL 都围绕事件流设计,而不是只返回最终字符串。
5. 状态存储要有 CAS
多副本生产环境中,状态写入必须考虑版本冲突和合并策略。
6. 外部副作用业务幂等
Runtime 可以保存状态,但无法替代订单、支付、邮件等业务系统的幂等表。
面试讲解话术
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 和工程套件。
架构师自测题
- AgentScope Java 为什么不能只理解成 ReAct Demo?
AgentBase.runLifecycle()解决了哪些通用运行时问题?- 为什么
RuntimeContext要通过 Reactor Context 传递? - 同一个
userId/sessionId的调用为什么需要串行化? - 模型输出的
ToolUseBlock如何变成真正工具执行? Toolkit为什么设计成 facade,而不是一个简单工具列表?PermissionEngine的优先级是什么?为什么 deny 要最高?- HITL 暂停后,下一次
call()如何恢复? AgentStateStore.saveIfVersion()解决什么问题?- 为什么 AgentStateStore 不能保证支付、发邮件等外部副作用 exactly-once?
- 结构化输出为什么需要 Native 与 synthetic tool 两条路径?
HarnessAgent相比裸ReActAgent增加了哪些生产能力?- 分布式部署中为什么需要
SandboxExecutionGuard? - 如果用 Java 自研 Agent Runtime,你会复用哪些设计?
HarnessAgent.call() 讲到 AgentBase.runLifecycle(),再讲到 ReActAgent.reasoning()、Toolkit.callTools()、AgentStateStore.save(),并能说明生产环境的状态恢复边界和幂等责任。