AgentScope Java新手教程:首个Agent基础对话详解

2026-06-11阅读 0热度 0
其他

第二章 HarnessAgent 与流式对话:基于 DeepSeek 打造首个具备推理能力的 Agent

2.1 极简启动示例

下面提供一个可直接运行的极简对话 Agent 示例,完整代码已附上:

【AgentScope Java新手村系列】(2)第一个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)
        //- call() 是简化的同步入口(返回 Mono)
        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 的两种构建方式 —— ReActAgentHarnessAgent 对比

AgentScope 2.0 提供两种 Agent 创建路径,根据场景灵活选择:

// 纯 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();

核心差异速览:

维度ReActAgentHarnessAgent
推理循环✅(继承父类)
工具调用
会话持久化❌(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(Project Reactor 响应式类型),.block() 将异步操作转为同步等待。
  • RuntimeContext.empty() 指不带身份信息的空上下文。生产环境应至少填充 sessionIduserId,详情见第五章。
  • 从 2.0 起,多次 call() 调用只要使用相同 sessionId,历史记录自动恢复,无需手动调用 memory.add(msg)

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");

        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 + "");
        }
    }
}

启动后即可与 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推荐新代码;仅关注父 Agent 自身事件(文本增量、工具调用、生命周期)
agent.stream(messages, opts, ctx)Flux@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("[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 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 表示累积输出(从开头到当前全部内容)

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 blocks = response.getContent();

2.7.4 MsgRole 枚举

public enum MsgRole {
    USER,       // 用户消息
    ASSISTANT,  // Agent 回复
    SYSTEM,     // 系统消息(如系统提示词)
    TOOL        // 工具调用结果
}
免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策