智能体
概述
Agent(接口位于 io.agentscope.core.agent.Agent,默认实现是 ReActAgent)是 AgentScope 的核心抽象——一个推理-行动循环引擎,将模型、工具、权限系统、人机交互、上下文管理、中间件、状态管理和事件系统整合到一个统一接口中。
其主要职责包括:
接收输入消息或事件,调用工具完成任务
管理上下文(会话历史保存在
AgentState.getContext()中,可通过AgentStateStore自动持久化)在关键生命周期阶段提供中间件钩子,支持自定义逻辑
自动管理并发和串行工具执行
核心接口
Agent 接口常用的方法如下:
| 方法 | 描述 |
|---|---|
call(List<Msg>) / call(List<Msg>, RuntimeContext) | 运行推理-行动循环,返回 Mono<Msg> |
streamEvents(List<Msg>) / streamEvents(Msg) | 同 call,但以流式方式逐一产出 AgentEvent 对象 |
observe(Msg) / observe(List<Msg>) | 将消息添加到上下文,不触发推理(返回 Mono<Void>) |
ReActAgent 在此之上还提供 call(msgs, structuredOutputClass, runtimeContext) 等结构化输出重载,以及通过 RuntimeContext 传递 per-call 元数据的便捷入口。
主循环
智能体在每次 call 调用时运行推理-行动循环,下图展示了主要控制流程:
配置智能体
通过 ReActAgent.builder()...build() 创建智能体。.model(...) 既接受 ModelRegistry 解析的字符串 id(最常用、自动读取 env),也接受手动 builder 构造的 Model 实例(需要精细控制超时、自定义 endpoint 时用)。
字符串 model id(推荐)
显式 Model builder
配置 Toolkit / MCP
提示
ModelRegistry的字符串形式(<provider>:<model>)需要对应的模型扩展模块在 classpath 中。它支持dashscopeopenaideepseekanthropicgeminiollama,会自动从环境变量读取 API key(DASHSCOPE_API_KEYOPENAI_API_KEYDEEPSEEK_API_KEYANTHROPIC_API_KEY/GEMINI_API_KEY)。需要在长期运行场景下同时获得工作区、会话持久化、记忆压缩、子 agent 等能力,请改用HarnessAgent—— 它对ReActAgent做了一层薄包装,builder 接口大体一致。
参数说明
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
name | String | 必填 | 智能体标识符,用于消息和日志 |
sysPrompt | String | 必填 | 智能体的基础系统提示词 |
model | Model | 必填 | 用于推理的大语言模型(继承自 ChatModelBase) |
toolkit | Toolkit | new Toolkit() | 管理工具、MCP 客户端、技能和工具组 |
middlewares | List<? extends MiddlewareBase> | List.of() | 应用于 agent reasoning acting model call system prompt 钩子 |
stateStore | AgentStateStore | null(不持久化) | 配置后 agent 在每次 call 后自动加载/保存 AgentState,按该次调用 RuntimeContext 的 (userId, sessionId) 寻址 |
defaultSessionId | String | agent name | 当某次调用的 RuntimeContext 没带 sessionId 时的兜底值 |
permissionContext | PermissionContextState | 默认 DEFAULT 模式 | 工具执行的细粒度规则,参见 权限系统 |
modelConfig | ModelConfig | 默认值 | 模型重试次数和备用模型 |
reactConfig | ReactConfig | 默认值 | 最大迭代次数和拒绝处理方式 |
maxIters | int | 10 | ReAct 主循环最大迭代次数(也可放在 reactConfig 中) |
多用户 / 多会话并发
ReActAgent 在调用之间是无状态的——同一个 agent 实例可以同时服务多个用户和会话。每次 call() 通过 RuntimeContext 携带的 (userId, sessionId) 来定位该次调用应该使用哪份对话状态,互不干扰。
每次 call() 开始时,agent 根据 RuntimeContext 中的 (userId, sessionId) 自动加载对应的 AgentState(对话上下文、权限规则等);call 结束后自动保存。不同 session 的状态完全隔离。
提示
同一个
(userId, sessionId)的调用会按到达顺序串行化执行——第二个请求等待第一个完成后再开始。不同 session 的调用可以完全并行。
Spring Boot 完整示例见 agentscope-examples/documentation/.../streaming/StreamingWebExample.java。
中断执行(Interrupt)
当需要从外部中断一个正在运行的 agent call 时(用户取消、超时、优雅停机),使用 interrupt:
中断是 per-session 的:只影响指定 (userId, sessionId) 的 in-flight call,不会波及同一 agent 上其他 session 的并发请求。
中断后的行为:
当前推理/工具执行在下一个检查点(reasoning 开始、acting 开始、streaming 每个 chunk)被拦截
agent 返回一个带
GenerateReason.INTERRUPTED标记的 Msg对话上下文(AgentState)自动保存——下次对同一 session 发起
call()时从中断点恢复
也可以直接用 (userId, sessionId) 字符串:
运行智能体
call 和 streamEvents 都接受相同的输入消息列表,驱动相同的推理-行动循环,区别在于结果的交付方式。
call
call 在内部消费所有事件,当智能体完成或因外部交互暂停时返回最终 Msg。
streamEvents
streamEvents 逐一产出 AgentEvent 对象,让你实时将文本输出、工具调用进度和生命周期事件流式传输给用户。按 event.getType() 分发即可针对每类事件做不同处理:
完整事件类型与字段参考 消息与事件。
observe
使用 observe 将消息注入智能体上下文而不触发 reply——适用于多智能体场景中,一个智能体需要观察另一个智能体输出的情况。
RuntimeContext (per-call 上下文)
RuntimeContext(io.agentscope.core.agent.RuntimeContext)是 per-call 元数据袋:每次 call / stream 把一份实例传进去,agent 在执行期间把它绑定到自身,下游的工具、middleware、hook 都能读到同一份引用;调用结束后自动解绑。
它不是持久化状态——AgentState(聊天上下文、压缩摘要、权限规则、tool state)才是。RuntimeContext 的作用是承载「当前这一次调用」相关的瞬态数据:tenant userId request-id、DB 连接、审计 logger、特性开关,等等。
内置字段与属性层
RuntimeContext 有三类「槽位」:
| 槽位 | 设置方式 | 读取方式 |
|---|---|---|
| 会话字段 | sessionId(String) / userId(String) | getSessionId() / getUserId() |
| 字符串属性(任意 key-value) | put(String key, Object value) | <T> T get(String key) |
类型化属性(按 Class<T> 注入业务 POJO) | put(Class<T> type, T value) / put(String key, Class<T> type, T value) | <T> T get(Class<T> type) / <T> T get(String key, Class<T> type) |
类型化属性是给 tool 用的——@Tool 方法里声明同类型参数即可被框架自动注入,详见 Tool — 接收 Context。字符串属性通常用于内部协调(例如 middleware 之间传值)。两层互不串扰:类型化层放进去的对象不会出现在 getExtra() 里,反之亦然。
构造并传入
ReActAgent 提供 call / stream 的 RuntimeContext 重载;streamEvents 未直接重载,需要传 context 时改用 stream(msgs, options, ctx) 或先在 builder 上配置全局 toolExecutionContext。不传 context 时框架使用 RuntimeContext.empty(),会话字段为 null,属性表为空,此时 agent 回退到 builder 上配置的 defaultSessionId。
谁能读到
Tool(
@Tool方法或ToolBase.callAsync)—— 见 Tool — 接收 Context。Middleware(
MiddlewareBase所有 hook)—— 作为第二个参数ctx直接传入。详见 Middleware — 读取 RuntimeContext。同一次调用的所有线程——
RuntimeContext内部使用ConcurrentMap,hook / tool 之间可以读写同一实例做协调。
与持久化的关系
RuntimeContext的自由 / 类型属性不会进AgentState,也不会被AgentStateStore写回磁盘。sessionId/userId字段会驱动持久化:每次调用激活对应的(userId, sessionId)状态槽位,因此在RuntimeContext上传不同身份就会切换加载/保存的AgentState。不传时回退到 builder 上配置的defaultSessionId。
完整示例:agentscope-examples/documentation/.../context/RuntimeContextExample.java、tool/ToolExecutionContextExample.java。
注意
存在一个旧的
ToolExecutionContext(io.agentscope.core.tool),已标记@Deprecated,新代码统一使用RuntimeContext。它在底层会被自动桥接到RuntimeContext.asToolExecutionContext(),老代码不会立即失效。
人机交互
当智能体遇到以下两种情况时,会暂停执行并发出特殊事件:需要用户确认的工具调用(权限系统返回 ASK),或标记为外部执行的工具(结果必须来自智能体外部)。两种情况下,都可以通过把结果事件再次喂给 agent 的下一次 call 来恢复执行。
用户确认
当权限系统判断某个工具调用需要用户批准时,智能体会发出 RequireUserConfirmEvent 并暂停。
1. 接收 RequireUserConfirmEvent —— 用 streamEvents 监听暂停。事件携带 getReplyId()(用于恢复)和 getToolCalls() —— 一组 ToolUseBlock,每个暴露 getId() getName() getInput() / getSuggestedRules()。
2. 构建确认结果 —— 为每个待处理工具调用构造一个 ConfirmResult。可以在传回前修改工具输入,或接受 suggested rules 让今后相同的调用自动放行:
3. 恢复智能体 —— 将 confirmResults 通过 metadata 传给下一次 call:
已确认的工具调用立即执行,智能体继续推理。
已拒绝的工具调用会产生 LLM 可见的错误结果,LLM 可能会用不同方式重试。
已接受的规则会持久化到权限引擎中——匹配的未来调用将自动允许,无需再次提示。
外部工具执行
当智能体调用 isExternalTool() == true 的工具时,会发出 RequireExternalExecutionEvent 并暂停。工具的逻辑在智能体外部运行——通常由人工操作员或外部系统执行。
1. 接收 RequireExternalExecutionEvent —— 结构与用户确认一致:getReplyId() 加一组等待外部执行的 getToolCalls()。
2. 外部执行并构建结果 —— 在智能体外部完成操作,把每个结果封装为 ToolResultBlock:
3. 恢复智能体 —— 将结果作为下一次 call 的输入消息回传。结果校验通过后会被注入智能体上下文,agent 会先发出 ExternalExecutionResultEvent,其 getReplyId() 与之前的 RequireExternalExecutionEvent#getReplyId() 相同,然后从中断处继续推理。
提示
构建交互式 UI 时使用
streamEvents——它可以实时检测暂停事件并立即提示用户。以编程方式处理事件的自动化流程则使用call。完整可运行示例见agentscope-examples/documentation/.../hitl/PermissionHITLExample.java。
配置状态持久化(AgentStateStore)
AgentState 是 agent 的全部可恢复状态——对话上下文、压缩摘要、权限规则、工具状态和当前 reply 位置。AgentStateStore 是它的存储抽象。
只需在 builder 上配 stateStore(...),agent 就会自动持久化与恢复:每次 call 结束把 AgentState 写回,下次用同一 (userId, sessionId) 调用时自动加载。Agent 实例本身对 session 无状态——具体读写哪个槽位由该次调用的 RuntimeContext 决定(缺省回退到 defaultSessionId)。
内置与扩展实现:
| 实现 | 模块 | 适用 |
|---|---|---|
InMemoryAgentStateStore | agentscope-core | 单元测试 / 单进程 demo |
JsonFileAgentStateStore | agentscope-core | 单机开发,按 (userId, sessionId) 分目录落 JSON |
RedisAgentStateStore | agentscope-extensions-redis | 多副本生产,跨进程跨机器共享 |
MysqlAgentStateStore | agentscope-extensions-mysql | 需要落关系型库(审计 / 报表) |
大多数场景只用一个 sessionId 就够;要按用户分桶就在 RuntimeContext 上同时设置 userId,存储会按 (userId, sessionId) 二元组寻址每个槽位。
通过 agent.getAgentState(userId, sessionId) 或 agent.getAgentState(runtimeContext) 可读取指定会话的状态快照:
完整字段、跨节点接续见上下文与 AgentState;压缩 Plan Mode 子 agent 的协作细节见上下文压缩。
结构化输出
结构化输出让智能体按照你指定的 JSON Schema 返回结果,而不是自由文本。适用于需要程序化消费 agent 输出的场景——表单填写、数据提取、决策分类等。
基本用法
传一个 Java 类(或 JsonNode schema)给 call 即可:
结构化输出与工具可以同时使用——智能体会先调用工具完成任务,最后以指定 schema 输出最终结果。
工作原理
框架根据模型能力自动选择实现路径:
| 路径 | 条件 | 行为 |
|---|---|---|
| 原生路径 | 模型支持 response_format + tools 并行(OpenAI、DashScope 等) | 将 JSON Schema 通过 response_format 直接传给模型 API,模型保证输出合法 JSON,循环自然结束 |
| 降级路径 | 模型不支持原生结构化输出(Anthropic、Ollama 等) | 注入 generate_response 合成工具 + 指令提示,模型以 tool call 方式输出结构化结果 |
无论哪条路径,调用方的代码完全相同——路径选择对用户透明。
从结果中读取数据
call 返回的 Msg 在 metadata 中携带解析后的结构化数据:
使用 JsonNode Schema
如果不想定义 Java 类,可以直接传 JSON Schema:
更多能力
以下能力均通过 builder 配置,详情参见各自的文档页面:
模型容错
技能系统(Skills)
技能是可热加载的 Markdown 提示词模块,运行时由 LLM 按需激活:
内置工具
| Builder 方法 | 说明 |
|---|---|
enableMetaTool(true) | 注册 list_tools / activate_group 元工具,让 LLM 能发现和切换工具组 |
enableTaskList() | 注册任务列表工具,让 LLM 拆解复杂任务为步骤并逐步完成 |
延伸阅读
权限系统
控制智能体可以调用哪些工具以及在什么条件下调用。
中间件
在 agent、reasoning、acting 和 model call 钩子处拦截和修改智能体行为。