03-AgentScope核心概念全景:Agent / Message / Model 与 ReAct

系列第 3 篇 · 启航篇

上两篇我们做到了:知道 AgentScope Java 是什么、还亲手跑通了第一个 HarnessAgent。但说实话------它到底是怎么"想"的?消息在内部怎么流转?模型又是怎么被调用的?

如果你对这三个问题还是模糊的,那后面讲工具调用、记忆、RAG、多 Agent 时,你只能"照着抄代码",一旦报错就无从下手。

所以这一篇,我们把镜头彻底拉远,讲清楚支撑一切的四个核心概念:Agent、Message、Model、ReAct 循环,外加 2.0 一个很 Java 工程师胃口的理念------透明开发。理解了它们,后面所有能力你都会"知其所以然"。


一、先建立一张全景图

AgentScope 的世界里,有四类核心抽象,它们的关系一句话就能说清:

Agent 在 ReAct 循环里,不断用 Message 与 Model 对话,直到产出最终答案。

sql 复制代码
graph TD
    A[Agent 智能体] -->|运行于| R[ReAct 循环]
    R -->|用 Message 与| M[Model 大脑]
    R -->|产出/消费| MSG[Message 消息]
    A -->|两种形态| RA[ReActAgent推理循环]
    A -->|两种形态| HA[HarnessAgent工程封装]

Agent是"执行者",负责把任务跑完;

ReAct是它内部的"运转机制",决定每一步是继续推理还是调用工具;

Message是 Agent 与模型、Agent 与 Agent 之间流通的"通用语言";

Model是 Agent 的"大脑",负责真正的推理与生成。

记住这张图,后面每篇都是在给其中一块"添砖加瓦"。


二、Agent:智能体的两种形态

在 2.0 里,"Agent"不是只有一个类,而是分两层,这也是 2.0 最重要的设计取舍:

1.ReActAgent------ 推理核心

它实现了 ReAct 算法(Reasoning + Acting 循环),负责"这一次推理怎么跑":纯推理、工具调用、记忆读写等原子能力都长在它上面。如果你只是想理解原理、或写个一次性脚本,用它就够了。

2.HarnessAgent------ 工程外壳(2.0 推荐)

它在 ReActAgent 之上,负责"长期运行的 Agent 怎么稳定、安全、可扩展":工作区、会话持久化、长期记忆、上下文压缩、沙箱隔离、权限控制......这些都是通过 Middleware / Toolkit叠加上去的,关键点在于它不改写推理循环。

2.0 一句话定位:推理归 ReAct,运行归 Harness。两者职责分离,所以你既能享受稳定工程能力,又不必担心底层推理被改坏。

cs 复制代码
// 2.0 推荐:直接 HarnessAgent 起步
HarnessAgent agent = HarnessAgent.builder()
        .name("assistant")
        .sysPrompt("你是一个有用的 AI 助手。")
        .model("dashscope:qwen-plus")              // ModelRegistry 解析
        .workspace(Paths.get(".agentscope/workspace"))
        .build();

(如果你只想要"裸推理"做实验,把 HarnessAgent 换成 ReActAgent、workspace(...) 去掉即可,其余写法一致。)


三、Message:Agent 之间的"通用语言"

这是 2.0 变化最大、也最值得理解的一块。很多 1.x 教程里把消息当"一句话",2.0 把它升级成了结构化的对话轮次。

3.1 一个关键认知

Msg(包 io.agentscope.core.message)不是"一句话",而是完整的一轮对话------可以是用户的一句输入、Agent 的一次回复、或一条系统指令。特别重要:一条 assistant 消息对应一次完整的call周期(内含多轮"推理 + 行动",直到最终回复)。

3.2 内容由"内容块"组成

2.0 里消息内容是有序的、带类型的 ContentBlock 列表,而不是一坨字符串。常见块类型:

| 块类型 | 说明 | 出现在哪些角色 | | --- | --- | --- | | TextBlock | 纯文本 | USER / ASSISTANT / SYSTEM | | DataBlock | 多模态:图片/音频/视频(base64 或 URL) | USER / ASSISTANT | | ThinkingBlock | 模型的思维链(推理过程) | ASSISTANT | | ToolUseBlock | 一次工具调用(id / name / input / state) | ASSISTANT | | ToolResultBlock | 工具执行结果 | ASSISTANT | | HintBlock | 以"用户上下文"形式注入循环的指令 | ASSISTANT |

角色约束在构造时就强制生效:USER 只允许文本/数据/媒体块,SYSTEM 只允许 TextBlock,ASSISTANT 则全部允许。

3.3 角色固定子类(2.0 推荐写法)

2.0 推荐直接用按角色固定的子类,而不是通用的 Msg.builder():

java 复制代码
import io.agentscope.core.message.UserMessage;
import io.agentscope.core.message.SystemMessage;
import io.agentscope.core.message.AssistantMessage;
 
// 便捷构造:普通字符串会自动包成 TextBlock
UserMessage userMsg  = new UserMessage("你好,请介绍一下自己");
SystemMessage sysMsg = new SystemMessage("system", "你是一个乐于助人的助手。");
AssistantMessage asstMsg = new AssistantMessage("agent", "好的,我是......");
 
// 读取内容
String text = userMsg.getTextContent();   // 所有 TextBlock 用 \n 拼接

多模态也不复杂,直接往里塞 DataBlock 即可:

bash 复制代码
UserMessage multi = new UserMessage(
        "user",
        TextBlock.builder().text("描述这张图:").build(),
        DataBlock.builder()
                .source(Base64Source.builder()
                        .data("...base64...")
                        .mediaType("image/png")
                        .build())
                .build());

小贴士:2.0 用 DataBlock 统一替代了旧的 ImageBlock/AudioBlock/VideoBlock,新代码建议直接用 DataBlock。


四、Model:Agent 的"大脑"

Model 负责真正的推理与生成。AgentScope 用 Model 接口(各厂商聊天模型继承 ChatModelBase 抽象类)把各家大模型 API 的差异屏蔽掉,让你写业务时不用关心"这是通义千问还是 OpenAI"。

4.1 2.0 新写法:字符串模型

2.0 引入了最省事的写法------字符串模型,由 ModelRegistry 解析并自动读取对应的 API Key(如 DASHSCOPE_API_KEY):

php 复制代码
.model("dashscope:qwen-plus")   // qwen-max / qwen-turbo 同理

4.2 需要精细控制时:传 Model 实例

字符串只能跑默认配置。如果你的场景需要指定温度、超时、代理等,2.0 仍然支持直接传 Model 实例(例如 DashScopeChatModel.builder()...build())------.model()既收字符串、也收Model实例,这点不要被"字符串写法"误导。

4.3 Formatter:你不用关心的适配层

不同厂商的 API 格式天差地别,AgentScope 用 Formatter 在内部自动转换消息格式(DashScopeChatFormatter、OpenAIChatFormatter、AnthropicChatFormatter、GeminiChatFormatter......)。通常你不用手动配,框架按 Model 类型自动选。

一句话:2.0 里"换模型" ≈ 改一个字符串前缀(dashscope: / deepseek: / openai:),前提是装了对应的 agentscope-extensions-model-* 扩展包。


五、ReAct 循环:Agent 到底怎么"思考"

这是整个框架的发动机,也是理解一切能力的前提。

ReAct = Reasoning(推理)+ Acting(行动)。它不像传统"写死的流程图"那样一步步走,而是让模型动态决策:这一步是该直接回答,还是该先调个工具拿数据再说。

一次 call 的典型循环:

sql 复制代码
flowchart TD
    Start([用户消息]) --> Think[Reasoning:模型推理]
    Think --> Decide{需要调用工具?}
    Decide -->|否| Answer([最终回复 / MODEL_STOP])
    Decide -->|是| Action[Acting:执行 Tool]
    Action --> Observe[Observation:拿到工具结果]
    Observe --> Think

用"查天气"举个具体例子:

Reasoning(思考):用户问"北京今天天气?"→ 模型判断:我得先查天气,不能直接编。

Acting(行动):调用 get_weather(city="北京")。

Observation(观察):工具返回"北京:晴,25℃"。

Reasoning 再思考:信息齐了,可以作答。

最终回复:"北京今天晴,25℃,注意防晒。"

如果信息还不够,模型会带着新观察再走一轮循环------这就是"自主规划、动态决策",也是它比刚性工作流强的地方。

每轮 call 结束时,框架会给出一个 GenerateReason(终止原因),常见的有:MODEL_STOP(正常答完)、TOOL_SUSPENDED(工具执行被挂起,常见于需要人工确认)、MAX_ITERATIONS(到达最大循环次数)、INTERRUPTED 等。生产代码一定要处理这些状态,别默认"每次都能拿到正常回答"。


六、透明开发:用 Middleware 窥探 ReAct 全过程

企业落地的老大难是:Agent "黑盒决策",出了错查不到为什么。AgentScope 的解法是Middleware(中间件)体系------在 Agent 生命周期的 5 个关键执行点挂上你自己的逻辑(日志、监控、改消息、改系统提示词),把推理全过程"摊开"给你看。

2.0 的 MiddlewareBase 接口暴露了 5 个钩子("洋葱模型",before/after 包裹执行):

| 钩子 | 拦截点 | 常用场景 | | --- | --- | --- | | onAgent | 整个 Agent 调用 | 入口日志、鉴权 | | onReasoning | 推理 / 模型调用阶段 | 打印推理文本、耗时 | | onActing | 单次工具调用执行 | 打印工具入参 / 结果 | | onModelCall | 原始模型 API 调用 | 改请求、归一化输出 | | onSystemPrompt | 系统提示词(管道式) | 动态注入上下文 |

旧版 Hook 接口自 2.0.0 起已标记 @Deprecated(forRemoval = true),官方推荐统一改用 Middleware。

来看一个"调试中间件",把推理流式文本和工具调用实时打印出来------这就是"透明开发"最直观的体现:

typescript 复制代码
import io.agentscope.core.middleware.MiddlewareBase;
import io.agentscope.core.middleware.ReasoningInput;
import io.agentscope.core.middleware.ActingInput;
import io.agentscope.core.agent.Agent;
import io.agentscope.core.agent.RuntimeContext;
import io.agentscope.core.event.AgentEvent;
import io.agentscope.core.event.TextBlockDeltaEvent;
import io.agentscope.core.event.ToolResultEndEvent;
import java.util.function.Function;
import reactor.core.publisher.Flux;
 
MiddlewareBase debugMiddleware = new MiddlewareBase() {
    @Override
    public Flux onReasoning(
            Agent agent, RuntimeContext ctx, ReasoningInput input,
            Function> next) {
        System.out.println("\n[思考] Agent 开始推理 (session=" + ctx.getSessionId() + ")");
        return next.apply(input)
                .doOnNext(event -> {
                    if (event instanceof TextBlockDeltaEvent e) {
                        System.out.print(e.getDelta());      // 推理流式文本增量
                    }
                })
                .doOnComplete(() -> System.out.println("\n[思考] 推理结束"));
    }
 
    @Override
    public Flux onActing(
            Agent agent, RuntimeContext ctx, ActingInput input,
            Function> next) {
        input.toolCalls().forEach(tc ->
                System.out.println("[行动] 执行工具 → " + tc.getName()));
        return next.apply(input)
                .doOnNext(event -> {
                    if (event instanceof ToolResultEndEvent e) {
                        System.out.println("[行动] 工具 " + e.getToolCallName()
                                + " 结果状态=" + e.getState());
                    }
                });
    }
};
 
// 挂到 Agent 上(HarnessAgent / ReActAgent 通用)
HarnessAgent agent = HarnessAgent.builder()
        .name("assistant")
        .sysPrompt("你是一个有用的 AI 助手。")
        .model("dashscope:qwen-plus")
        .workspace(Paths.get(".agentscope/workspace"))
        .middleware(debugMiddleware)   // 2.0 推荐:用 Middleware 而非已弃用的 Hook
        .build();

中间件的返回值走 Project Reactor(Flux<AgentEvent> / Mono<String>),生产里你完全可以把这些事件推到 OpenTelemetry 或 AgentScope Studio 做可视化调试------更完整的可观测能力我们放到"生产篇"再展开。


七、新手最常误解的 5 个点

把Msg当成"一句话"------ 它是完整的"一轮对话",一条 assistant 消息可能内含多轮推理+工具调用。

还在用Msg.builder()通用写法------ 2.0 推荐角色固定子类(UserMessage/AssistantMessage/SystemMessage/ToolResultMessage),约束更清晰、不易写错。

以为.model()只认字符串------ 它也能收 ChatModel 实例,需要精细配置温度/超时/代理时用后者。

非 DashScope 模型却没装扩展包------ dashscope: 适配器也要单独引 agentscope-extensions-model-dashscope,并非 harness 内置;换 DeepSeek / OpenAI 等同样要补对应的 agentscope-extensions-model-*。

忽视GenerateReason------ 别假设每次都 MODEL_STOP;TOOL_SUSPENDED、MAX_ITERATIONS 等中断状态要在业务里妥善处理。


小结

这一篇你建立起了 AgentScope Java 2.0 的心智模型:

Agent分两层:ReActAgent(推理核心)与 HarnessAgent(工程外壳,2.0 推荐);

Message是结构化对话轮次,由带类型的 ContentBlock 组成,2.0 推荐角色固定子类;

Model是大脑,ChatModel 屏蔽厂商差异,2.0 用字符串 + ModelRegistry 极简接入;

ReAct 循环= 推理→行动→观察 的不断迭代,支持动态决策与自主规划;

透明开发靠 Middleware 把推理全过程"摊开"给你看,这是企业落地可控性的关键。

理解这四块,后面所有能力------工具、记忆、RAG、多 Agent------本质上都是"给 Agent 接更多 Message 来源 / 更多 Model 能力 / 更丰富的循环分支"。

下一篇,我们就从最实用的一块切入:让 Agent 真正"动手"------Tool Calling 工具调用实战。你会看到 ReAct 循环里那个 "Acting" 步骤,是怎么被你自己的 Java 方法填满的。

下篇预告:《让 Agent 会"用工具":Tool Calling 实战》


本篇基于 AgentScope Java 2.0。API 与概念以官方文档(java.agentscope.io)最新 2.0.x 为准,建议结合对应版本动手实验。

相关推荐
goehou3 分钟前
Agent 卡在第三步,怎么查,一次请求的可观测性
llm·agent·日志·trace·可观测性
Morning的Tech4 分钟前
Codex 中智能体的执行机制分析和对比
agent
goehou8 分钟前
Agent 把用户地址发给了模型,数据脱敏该在哪一层做
llm·agent·数据脱敏·隐私合规·工程实践
frcoder1 小时前
TackHarness打造属于自己的编程工作流
agent·ai编程·1024程序员节·研发·harness
墨心@2 小时前
PI分层设计的经验
agent·pi·coding agent·harness
羞儿2 小时前
【DSH】创造模式与内核边界:一切皆插件的递归与自证
agent·1024程序员节·harness
编程的一拳超人2 小时前
【元宝搜索开发者征文大赛】四语实战:Java/Python/Go/Node.js 构建 AI 搜索应用全攻略
java·python·golang·node.js·agent
lucas_AI3 小时前
把网线拔了,OpenAI 的 AI 智能体还是聊上了:CPU 缓存成了密道
agent