【AgentScope Java新手村系列】(2)第一个Agent-基础对话

简介: 第一个Agent-基础对话 — 演示 HarnessAgent 的 Builder 模式创建、ReAct 推理循环、流式事件与思考模式三个核心能力。

第二章 HarnessAgent 与流式对话:用 DeepSeek 跑通第一个会思考的 Agent

"ReAct(推理-行动)是 Agent 框架的核心范式。本章用 OpenAIChatModel 接入 DeepSeek,演示 HarnessAgent 的 Builder 模式、流式事件、思考模式三个关键能力——从此 LLM 真正'活'起来。"

2.1 最简示例

这一章从零开始构建一个能对话的 Agent。完整代码如下:

package com.example;

import io.agentscope.core.ReActAgent;
import io.agentscope.core.agent.RuntimeContext;
import io.agentscope.core.formatter.openai.OpenAIChatFormatter;
import io.agentscope.core.message.UserMessage;
import io.agentscope.core.model.GenerateOptions;
import io.agentscope.core.model.OpenAIChatModel;
import io.agentscope.core.tool.Toolkit;
import io.agentscope.harness.HarnessAgent;

import java.nio.file.Path;

public class BasicChatExample {
   
    public static void main(String[] args) {
   
        String apiKey = System.getenv("DEEPSEEK_API_KEY");

        // 创建 Model
        OpenAIChatModel model = OpenAIChatModel.builder()
                .apiKey(apiKey)
                .modelName("deepseek-chat")
                .baseUrl("https://api.deepseek.com")
                .stream(true)
                .enableThinking(true)
                .formatter(new OpenAIChatFormatter())
                .defaultOptions(GenerateOptions.builder()
                        .thinkingBudget(1024)
                        .build())
                .build();

        // 方式 A:纯 ReActAgent(最轻量,仅一个推理循环)
        ReActAgent plain = ReActAgent.builder()
                .name("Assistant")
                .sysPrompt("你是一个乐于助人的AI助手,请友好简洁地回答问题。")
                .model(model)
                .toolkit(new Toolkit())
                .build();

        // 方式 B:HarnessAgent(推荐——开箱即用:工作区、Session、记忆、子 agent、压缩…)
        HarnessAgent agent = HarnessAgent.builder()
                .name("Assistant")
                .sysPrompt("你是一个乐于助人的AI助手,请友好简洁地回答问题。")
                .model(model)
                .workspace(Path.of("./workspace"))
                .build();

        // 构造用户消息 —— 2.0 推荐用具体子类型
        UserMessage userMsg = new UserMessage("你好,请介绍一下自己");

        // 调用 Agent 并获取回复
        //  - streamEvents() 是 2.0 推荐的流式 API(返回 Flux<AgentEvent>)
        //  - call() 是简化的同步入口(返回 Mono<Msg>)
        String reply = agent.call(userMsg, RuntimeContext.empty())
                .block()
                .getTextContent();
        System.out.println(reply);
    }
}

2.2 代码拆解

创建 Model

OpenAIChatModel.builder()
        .apiKey(apiKey)                    // API Key
        .modelName("deepseek-chat")        // 模型名称
        .baseUrl("https://api.deepseek.com")
        .stream(true)                      // 启用流式输出
        .enableThinking(true)              // 启用思考模式(类似 DeepSeek 的思考链)
        .formatter(new OpenAIChatFormatter())  // 格式化器,负责将消息转换为 API 格式
        .defaultOptions(GenerateOptions.builder()
                .thinkingBudget(1024)       // 思考 token 预算
                .build())
        .build()

OpenAIChatModel 使用 Builder 模式创建。关键配置:

  • apiKey:LLM 服务的 API Key(DeepSeek 用 DEEPSEEK_API_KEY,OpenAI 用 OPENAI_API_KEY
  • modelName:模型标识,DeepSeek 用 deepseek-chatdeepseek-reasoner,OpenAI 用 gpt-4o
  • stream:是否使用流式输出
  • enableThinking:是否启用思考模式,启用后 Agent 会先"思考"再回答
  • formatter:格式化器,不同模型提供商有不同的格式化器

创建 Agent —— ReActAgent vs HarnessAgent

2.0 有两个入口类,按场景二选一:

// 纯 ReActAgent:一个推理循环,无任何工程能力
ReActAgent.builder()
        .name("Assistant")
        .sysPrompt("你是一个乐于助人的AI助手,请友好简洁地回答问题。")
        .model(model)
        .toolkit(new Toolkit())
        .build();
// HarnessAgent(推荐):在 ReActAgent 之上叠加了工作区、Session、记忆、子 agent、压缩…
// 不开任何额外能力时行为等价于裸 ReActAgent;按需打开:
HarnessAgent.builder()
        .name("Assistant")
        .sysPrompt("你是一个乐于助人的AI助手,请友好简洁地回答问题。")
        .model(model)
        .workspace(Path.of("./workspace"))      // 必填:Harness 需要工作区根目录
        // .stateStore(...)                     // 可选:分布式 AgentStateStore 后端(Redis/MySQL…)
        // .compaction(...)                      // 可选:上下文压缩
        // .subagent(...)                        // 可选:声明子 agent
        // .skillRepository(...)                 // 可选:技能仓库
        // .enablePlanMode()                     // 可选:Plan Mode(HITL 退出)
        .build();

对比要点

维度 ReActAgent HarnessAgent
推理循环 ✅(继承)
工具调用
会话持久化 ❌(1.x 靠 Memory,2.0 需自己接 Session ✅ 默认 WorkspaceSession,可换 Redis/MySQL
工作区 / 长期记忆
上下文压缩 ✅ 按需打开
子 agent 编排
沙箱隔离
适用场景 学习 ReAct 原理、写单次脚本 生产/长期运行的 agent

构造消息

2.0 推荐用具体子类型而不是通用 Msg.builder()

import io.agentscope.core.message.UserMessage;

UserMessage userMsg = new UserMessage("你好,请介绍一下自己");

四种角色都有对应子类型:UserMessage / AssistantMessage / SystemMessage / ToolResultMessage。每个子类型都有便捷构造函数和 Builder,可以附加多模态块(TextBlock / DataBlock / ToolUseBlock / ToolResultBlock)。

如果需要更细的控制(比如多块、命名发送方),仍可以用 Msg.builder()

import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.TextBlock;

Msg userMsg = Msg.builder()
        .name("alice")
        .role(MsgRole.USER)
        .textContent("你好")                  // 单文本快捷方式
        .build();
// 等价于:
Msg userMsg2 = Msg.builder()
        .name("alice")
        .role(MsgRole.USER)
        .content(TextBlock.builder().text("你好").build())
        .build();

调用 Agent

import io.agentscope.core.agent.RuntimeContext;

String reply = agent.call(new UserMessage("你好"), RuntimeContext.empty())
        .block()
        .getTextContent();
  • agent.call(messages, ctx) 返回 Mono<Msg>(Project Reactor 的响应式类型),.block() 将异步操作转为同步等待。
  • RuntimeContext.empty() 是不带任何身份信息的"裸"上下文。生产环境里至少填 sessionIduserId,详见第五章。
  • 2.0 起,多个 call() 之间通过同一个 sessionId 自动恢复历史;不再需要手动 memory.add(msg)

没有 RuntimeContext 这个参数的方法也存在(1.x 风格的 agent.call(msg)),但新代码请统一传 RuntimeContext——所有 middleware、hook、tool 都靠它读"这是谁在说话"。漏传会让 Session 持久化、权限规则、人机交互等全部失效。

2.3 多轮对话

会话历史由 Session 自动管理。每次调用 call() 时,只要 RuntimeContext 里的 sessionId 不变,就会自动接上次的上下文。

package com.example;

import io.agentscope.core.ReActAgent;
import io.agentscope.core.agent.RuntimeContext;
import io.agentscope.core.formatter.openai.OpenAIChatFormatter;
import io.agentscope.core.message.UserMessage;
import io.agentscope.core.model.OpenAIChatModel;
import io.agentscope.core.tool.Toolkit;
import io.agentscope.harness.HarnessAgent;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.file.Path;

public class MultiTurnChat {
   
    public static void main(String[] args) throws Exception {
   
        String apiKey = System.getenv("DEEPSEEK_API_KEY");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        OpenAIChatModel model = OpenAIChatModel.builder()
                .apiKey(apiKey)
                .modelName("deepseek-chat")
                .baseUrl("https://api.deepseek.com")
                .stream(true)
                .formatter(new OpenAIChatFormatter())
                .build();

        HarnessAgent agent = HarnessAgent.builder()
                .name("Assistant")
                .sysPrompt("你是一个乐于助人的AI助手,请友好简洁地回答问题。")
                .model(model)
                .workspace(Path.of("./workspace"))
                .build();

        System.out.println("=== Chat Started ===");
        System.out.println("Type 'exit' to quit\n");

        while (true) {
   
            System.out.print("You> ");
            String input = reader.readLine();

            if (input == null || "exit".equalsIgnoreCase(input.trim())) {
   
                System.out.println("Goodbye!");
                break;
            }

            if (input.trim().isEmpty()) {
   
                continue;
            }

            // 同一 sessionId 走同一份历史;这里是 demo 用固定值
            RuntimeContext ctx = RuntimeContext.builder()
                    .sessionId("demo-001")
                    .userId("alice")
                    .build();

            String reply = agent.call(new UserMessage(input), ctx)
                    .block()
                    .getTextContent();
            System.out.println("Agent> " + reply + "\n");
        }
    }
}

运行后可以进行多轮对话,Agent 会记住之前的对话内容。sessionId 是恢复的"钥匙"——同一个 sessionId 不管在哪个节点调用,都会拼回同一段历史。

2.4 切换模型

框架内置支持多种模型,切换只需更换 Model 实现。

使用 OpenAI

import io.agentscope.core.model.OpenAIChatModel;

OpenAIChatModel model = OpenAIChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("gpt-4o")
        .stream(true)
        .build();

使用 Anthropic Claude

import io.agentscope.core.model.AnthropicChatModel;

OpenAIChatModel model = AnthropicChatModel.builder()
        .apiKey(System.getenv("ANTHROPIC_API_KEY"))
        .modelName("claude-sonnet-4-20250514")
        .build();

使用 Google Gemini

import io.agentscope.core.model.GeminiChatModel;

OpenAIChatModel model = GeminiChatModel.builder()
        .apiKey(System.getenv("GEMINI_API_KEY"))
        .modelName("gemini-2.0-flash")
        .build();

使用 Ollama(本地模型)

// 2.0 不再有独立的 OllamaChatModel;用 OpenAIChatModel + Ollama 的 OpenAI 兼容端点
OpenAIChatModel model = OpenAIChatModel.builder()
        .apiKey("ollama")                   // Ollama 不校验 key
        .modelName("llama3")
        .baseUrl("http://localhost:11434")
        .build();

Ollama 不需要 API Key,但需要在本地运行 Ollama 服务。

使用 OpenAI 兼容接口

很多国产模型提供 OpenAI 兼容接口,可以通过 baseUrl 配置:

OpenAIChatModel.builder()
        .apiKey("your-api-key")
        .baseUrl("https://your-model-endpoint.com/v1/")
        .modelName("your-model-name")
        .build()

2.5 GenerateOptions 生成参数

GenerateOptions 控制 LLM 的生成行为:

import io.agentscope.core.model.GenerateOptions;

GenerateOptions options = GenerateOptions.builder()
        .temperature(0.7)        // 温度,控制随机性,0-2
        .topP(0.9)               // 核采样参数
        .maxTokens(2048)         // 最大输出 token 数
        .frequencyPenalty(0.0)   // 频率惩罚
        .presencePenalty(0.0)    // 存在惩罚
        .build();

// 作为 Model 的默认参数
OpenAIChatModel.builder()
        .apiKey(apiKey)
        .modelName("deepseek-chat")
        .baseUrl("https://api.deepseek.com")
        .defaultOptions(options)
        .build();

调用时也可以覆盖:

agent.call(userMsg, ctx, options).block();

2.6 流式输出

2.0 有两套流式 API:

API 返回类型 状态 适用场景
agent.streamEvents(messages, ctx) Flux<AgentEvent> 推荐 新代码;只关心父 agent 自身事件(文本增量、工具调用、生命周期)
agent.stream(messages, opts, ctx) Flux<Event> @Deprecated(forRemoval = true) 目前唯一能实时拿到子 agent 事件的入口;待 AgentEvent 子来源通道落地后也将迁移

AgentEvent 体系的关键事件类型(io.agentscope.core.event 包下):

事件 含义
AgentStartEvent / AgentEndEvent 一次 call() 的开始/结束
TextBlockDeltaEvent 文本增量(一个 token / 一段 chunk)
ReasoningEvent 思考过程(启用 enableThinking 后才有)
ToolCallStartEvent / ToolResultStartEvent 工具调用开始/工具结果开始
RequireUserConfirmEvent 需要用户确认(人机交互)

2.6.1 streamEvents() —— 2.0 推荐

import io.agentscope.core.event.AgentEvent;
import io.agentscope.core.event.AgentEventType;
import io.agentscope.core.event.TextBlockDeltaEvent;
import io.agentscope.core.event.ToolCallStartEvent;
import io.agentscope.core.message.UserMessage;
import io.agentscope.core.agent.RuntimeContext;

agent.streamEvents(new UserMessage("写一首关于秋天的诗"), RuntimeContext.empty())
        .doOnNext(event -> {
   
            if (event.getType() == AgentEventType.TEXT_BLOCK_DELTA) {
   
                System.out.print(((TextBlockDeltaEvent) event).getDelta());
            } else if (event.getType() == AgentEventType.TOOL_CALL_START) {
   
                ToolCallStartEvent start = (ToolCallStartEvent) event;
                System.out.println("\n[tool] " + start.getToolName());
            }
        })
        .blockLast();

streamEvents() 这条路径不会把子 agent 事件转发出来——子 agent 在静默运行,结果以 TOOL_RESULT 块的形式回给父 agent。详见第七章。

2.6.2 stream() —— 已弃用但目前唯一能拿到子 agent 事件的入口

import io.agentscope.core.agent.Event;
import io.agentscope.core.agent.EventType;
import io.agentscope.core.agent.StreamOptions;
import io.agentscope.core.message.UserMessage;
import io.agentscope.core.agent.RuntimeContext;
import reactor.core.publisher.Flux;

StreamOptions streamOptions = StreamOptions.builder()
        .eventTypes(EventType.REASONING, EventType.TOOL_RESULT, EventType.AGENT_RESULT)
        .incremental(true)                  // 增量 vs 累积
        .build();

Flux<Event> events = agent.stream(
        List.of(new UserMessage("写一首关于秋天的诗")),
        streamOptions,
        RuntimeContext.empty());

events.doOnNext(event -> {
   
    String text = event.getMessage() != null ? event.getMessage().getTextContent() : null;
    if (text != null && !text.isEmpty()) {
   
        System.out.print(text);
    }
}).blockLast();

StreamOptions 的配置:

  • eventTypes:订阅的事件类型(REASONING 思考过程、TOOL_RESULT 工具结果、AGENT_RESULT 最终结果)
  • incrementaltrue 表示增量输出(每次只输出新内容),false 表示累积输出(每次输出从头到当前的全部内容)

之所以保留 stream() 是因为它是目前唯一能在父流里实时看到子 agent 事件的入口。在 AgentEvent 子来源通道落地后,stream() 也会彻底退役。新写代码默认 streamEvents(),需要子 agent 事件时再切 stream()

2.7 Msg 的更多用法

2.7.1 快捷创建(具体子类型)

import io.agentscope.core.message.UserMessage;
import io.agentscope.core.message.SystemMessage;
import io.agentscope.core.message.AssistantMessage;
import io.agentscope.core.message.TextBlock;

UserMessage userMsg = new UserMessage("你好!");                       // 用户消息
SystemMessage sysMsg = new SystemMessage("你是一个乐于助人的助手。");  // 系统消息
AssistantMessage asstMsg = new AssistantMessage("你好!有什么可以帮你的吗?");            // Agent 回复

2.7.2 通用 Msg.builder()

import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;

// 最简写法(默认 USER 角色)
Msg msg = Msg.builder().textContent("你好").build();

// 指定角色
Msg systemMsg = Msg.builder()
        .role(MsgRole.SYSTEM)
        .textContent("你是一个乐于助人的助手。")
        .build();

2.7.3 读取消息内容

import io.agentscope.core.message.ContentBlock;
import java.util.List;

Msg response = agent.call(userMsg, ctx).block();

// 获取文本内容
String text = response.getTextContent();

// 获取角色
MsgRole role = response.getRole();

// 获取发送者名称
String name = response.getName();

// 获取所有内容块
List<ContentBlock> blocks = response.getContent();

2.7.4 MsgRole 枚举

public enum MsgRole {
   
    USER,       // 用户消息
    ASSISTANT,  // Agent 回复
    SYSTEM,     // 系统消息(如系统提示词)
    TOOL        // 工具调用结果
}
目录
相关文章
|
3小时前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
7194 31
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
3小时前
|
数据采集 人工智能 前端开发
让 Coding Agent 从黑盒到透明:阿里云 Agent 观测审计数据采集实践
AI Agent 规模化落地带来执行黑盒、行为难追溯、成本难度量三大难题。阿里云基于 OTel 标准,面向 Coding Agent、个人通用助理和框架型 Agent,推出 LoongSuite Pilot、插件及探针等无侵入采集方案,让 Agent 实现可看见、可分析、可审计、可治理。
625 140
|
3小时前
|
人工智能 缓存 自然语言处理
阿里Qwen3.7-Max评测:Agent能力显著提升,耗时与调用成本大幅下降
阿里云百炼推出面向智能体的旗舰大模型Qwen3.7-Max,具备长周期自主执行能力,显著提升编程、办公自动化等复杂任务处理水平;支持MCP集成与多框架兼容,并以限时5折+100万Tokens免费试用大幅降低使用门槛,助力企业高效落地AI应用。在阿里云百炼平台快速体验:https://t.aliyun.com/U/fPVHqY
|
3小时前
|
人工智能 弹性计算 运维
阿里云发布堡垒机智能运维Agent,运维交互进入自然语言新时代
支持自然语言运维,提升效率与安全双保障。
1158 1
|
3小时前
|
人工智能 安全 定位技术
CodeGraph深度解析 让Claude Code工具调用直降七成的核心原理与实操教程
如今以Claude Code为代表的AI编程智能体已经成为开发者日常编码、项目重构、漏洞修复的必备工具。但在长期使用过程中,几乎所有开发者都会遇到同一个明显痛点:AI虽然具备强大的代码生成与分析能力,却常常陷入盲目探索的循环中。
1222 2
|
3小时前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1296 3
|
3小时前
|
人工智能 弹性计算 安全
阿里云618活动时间、活动入口、优惠活动详细解读
2026年阿里云618创新加速季已全面开启,作为年度力度最大的云产品促销活动,本次大促覆盖轻量应用服务器、ECS云服务器、GPU云服务器、数据库、AI算力、安全服务、CDN等全品类产品,推出5亿元算力补贴、新用户限时秒杀、普惠满减、企业专享、免费试用、云大使返佣等多重福利,个人开发者、中小企业、AI团队均可享受专属低价。本文将系统梳理2026年阿里云618活动的完整时间节点、官方参与入口、各类优惠细则、使用规则、热门产品推荐及实操代码,帮助用户精准参与、高效省钱,以最低成本完成上云部署。
1044 5
|
3小时前
|
人工智能 自然语言处理 算法
|
3小时前
|
人工智能 自然语言处理 安全
Vibe Coding 实战:别盲目跟风,先分清 vibe coding 适合什么场景
本文系统总结vibe coding实战经验:明确其适用场景(原型、小工具、标准化模块),剖析5步落地流程(场景判定→结构化提示词→目录初始化→分模块生成→自动化校验),指出四大常见误区,并推荐适配工具Trae。强调“场景匹配+规则前置”是提效关键,避免盲目套用。
857 1
|
3小时前
|
人工智能 运维 API
2026年阿里云百炼通义千问Qwen3.7-plus深度介绍 功能特性、使用优势及618大促订阅方案指南
大模型技术的普及,让AI能力逐步融入个人办公、内容创作、代码编写、企业运营、教育培训等各类场景。不同定位的模型对应不同使用需求,旗舰级模型性能强劲但使用成本偏高,轻量化模型价格低廉却难以胜任复杂任务,而介于两者之间的中端主力模型,凭借均衡的能力、亲民的定价、广泛的场景适配性,成为绝大多数个人用户、小型团队、中小企业的首选。
406 1

热门文章

最新文章