Agent Scope-1.0

代码地址:https://gitee.com/CodeMao01/agentscope-learn

一、快速开始

1.1、studio启动

官网地址:https://java.agentscope.io/v1/zh/docs/task/observability.html

AgentScope Java 提供了多种机制来观测和调试智能体的执行过程:

  • Studio(可视化调试):Studio 提供 Web 界面实时可视化 Agent 执行过程,支持交互式调试和消息追踪。
  • OpenTelemetry 链路追踪:通过 OTLP 协议将 Trace 数据导出到外部可观测性平台(如 Langfuse、Jaeger)

核心特性:

  • 实时可视化:Web 界面展示 Agent 推理和执行过程
  • 交互式输入:通过 Web UI 与 Agent 对话
  • 消息追踪:查看完整的消息流和Trace
  • 多 Run 管理:支持多个实验运行的组织和比较
  1. 拉项目、安装依赖并启动
json 复制代码
git clone https://github.com/agentscope-ai/agentscope-studio
cd agentscope-studio
npm install
npm run dev
  1. 打开可视化界面:http://localhost:5173/overview

1.2、项目创建

  1. 项目整体目录
  1. 父子项目pom依赖
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.15</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>


    <groupId>com.example</groupId>
    <artifactId>agentscope-learn</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>agentscope-lern</name>
    <description>agentscope-learn</description>

    <modules>
        <module>quickstart</module>
    </modules>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
        <agentscope.version>1.0.10</agentscope.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.agentscope</groupId>
                <artifactId>agentscope-spring-boot-starter</artifactId>
                <version>${agentscope.version}</version>
            </dependency>
            <dependency>
                <groupId>io.agentscope</groupId>
                <artifactId>agentscope-extensions-studio</artifactId>
                <version>${agentscope.version}</version>
            </dependency>
            <dependency>
                <groupId>io.agentscope</groupId>
                <artifactId>agentscope-agui-spring-boot-starter</artifactId>
                <version>${agentscope.version}</version>
            </dependency>
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <version>5.3.2</version>
            </dependency>
            <dependency>
                <groupId>io.socket</groupId>
                <artifactId>socket.io-client</artifactId>
                <version>2.1.2</version>
            </dependency>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-api</artifactId>
                <version>1.49.0</version>
            </dependency>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-exporter-otlp</artifactId>
                <version>1.49.0</version>
            </dependency>
            <dependency>
                <groupId>io.opentelemetry.instrumentation</groupId>
                <artifactId>opentelemetry-reactor-3.1</artifactId>
                <version>2.25.0-alpha</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>io.agentscope</groupId>
            <artifactId>agentscope-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.42</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>agentscope-learn</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>quickstart</artifactId>
    <name>quickstart</name>
    <description>quickstart</description>

    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>io.agentscope</groupId>
            <artifactId>agentscope-extensions-studio</artifactId>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>io.socket</groupId>
            <artifactId>socket.io-client</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-api</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-exporter-otlp</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-reactor-3.1</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
  1. 启动类连接studio
java 复制代码
package com.example.quickstart;

import io.agentscope.core.studio.StudioManager;
import jakarta.annotation.PreDestroy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class QuickstartApplication {

    public static void main(String[] args) {
        // 初始化 Studio 连接
        StudioManager.init()
                .studioUrl("http://localhost:3000")
                .project("MyProject")
                .runName("demo_" + System.currentTimeMillis())
                .initialize()
                .block();

        SpringApplication.run(QuickstartApplication.class, args);
    }

    @PreDestroy
    public void destroy() {
        // 停止 Studio 运行
        StudioManager.shutdown();
    }

}
  1. 配置文件编写
yaml 复制代码
spring:
  application:
    name: quickstart
server:
  port: 8801
  servlet:
    encoding:
      enabled: true
      charset: UTF-8
      force: true
  1. ReActAgent配置
java 复制代码
package com.example.quickstart.config;

import io.agentscope.core.ReActAgent;
import io.agentscope.core.agent.Agent;
import io.agentscope.core.model.OllamaChatModel;
import io.agentscope.core.model.ollama.OllamaOptions;
import io.agentscope.core.model.ollama.ThinkOption;
import io.agentscope.core.studio.StudioManager;
import io.agentscope.core.studio.StudioMessageHook;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AIConfig {
    @Bean
    public OllamaChatModel ollamaChatModel() {
        return OllamaChatModel.builder()
                .modelName("qwen3.5:0.8b")
                .defaultOptions(OllamaOptions.builder()
                        .thinkOption(ThinkOption.ThinkBoolean.DISABLED).build())
                .build();
    }

    @Bean
    public Agent reactAgent() {
        return ReActAgent.builder()
                .name("Hello Agent")
                // studio 可观测这个agent
                .hook(new StudioMessageHook(StudioManager.getClient()))
                .model(ollamaChatModel())
                .build();
    }
}
  1. 测试接口
java 复制代码
package com.example.quickstart.controller;

import io.agentscope.core.agent.Agent;
import io.agentscope.core.agent.Event;
import io.agentscope.core.message.Msg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
public class HelloController {
    @Autowired
    private Agent agent;

    @GetMapping("/chat")
    public String chat(String question) {
        Msg block = agent.call(Msg.builder().textContent(question).build()).block();
        return block.getTextContent();
    }

    @GetMapping("/stream")
    public Flux<String> stream(String question) {
        Flux<Event> stream = agent.stream(Msg.builder().textContent(question).build());
        return stream.map(event -> event.getMessage().getTextContent());
    }
}
  1. 允许studio页面发送消息
java 复制代码
package com.example.quickstart.init;

import io.agentscope.core.agent.Agent;
import io.agentscope.core.message.Msg;
import io.agentscope.core.studio.StudioManager;
import io.agentscope.core.studio.StudioUserAgent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class StudioAgentRunner implements ApplicationRunner {

    @Autowired
    private Agent agent;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 创建用户 Agent
        StudioUserAgent user = StudioUserAgent.builder()
                .name("User")
                .studioClient(StudioManager.getClient())
                .webSocketClient(StudioManager.getWebSocketClient())
                .build();

        // 对话循环
        System.out.println("Starting conversation (type 'exit' to quit)");
        System.out.println("Open http://localhost:3000 to interact\n");

        Msg msg = null;
        int turn = 1;
        while (true) {
            System.out.println("[Turn " + turn + "] Waiting for user input...");
            msg = user.call(msg).block();

            if (msg == null || "exit".equalsIgnoreCase(msg.getTextContent())) {
                System.out.println("\nConversation ended");
                break;
            }

            System.out.println("[Turn " + turn + "] User: " + msg.getTextContent());
            msg = agent.call(msg).block();

            if (msg != null) {
                System.out.println("[Turn " + turn + "] Agent: "
                        + msg.getTextContent() + "\n");
            }
            turn++;
        }
    }
}

二、AgentScope

2.1、工具tool

2.1.1、快速使用

  1. 获取时间工具
java 复制代码
package com.example.quickstart.tool;

import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.ToolParam;

import java.time.LocalDateTime;

public class DateTools {
    @Tool(description = "获取当前时间")
    public String getCurrentDate(@ToolParam(name = "timeZone", description = "时区, 例如:北京") String timeZone) {
        return LocalDateTime.now().toString();
    }

}
  1. AgentScope接入工具
java 复制代码
@Bean
public Agent reactAgent() {
    Toolkit toolkit = new Toolkit();
    toolkit.registerTool(new DateTools());
    return ReActAgent.builder()
            .name("Hello Agent")
            // studio 可观测这个agent
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(ollamaChatModel())
            .toolkit(toolkit)
            .build();
}

2.1.2、工具配置

模型的工具配置和工具自定义配置,优先级是模型的工具配置

java 复制代码
@Bean
public Agent reactAgent() {
    Toolkit toolkit = new Toolkit(ToolkitConfig.builder()
            .executionConfig(ExecutionConfig.builder()
                    .maxAttempts(2)
                    .timeout(Duration.ofSeconds(1))
                    .build())
            .build());
    toolkit.registerTool(new DateTools());
    ExecutionConfig executionConfig = ExecutionConfig.builder()
            .timeout(Duration.ofSeconds(2))
//                .maxAttempts(3)
            .build();
    return ReActAgent.builder()
            .name("Hello Agent")
            // studio 可观测这个agent
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(ollamaChatModel())
            .toolkit(toolkit)
//                .maxIters(2)
            // 优先模型配置
            .toolExecutionConfig(executionConfig)
            .build();
}

2.1.3、参数传递

源码支持的参数:io.agentscope.core.tool.ToolMethodInvoker#convertParameters

java 复制代码
@Tool(description = "获取当前时间")
public String getCurrentDate(@ToolParam(name = "timeZone", description = "时区, 例如:北京") String timeZone,
                             AIConfig.UserContext userContext,
                             Agent agent,
                             ToolExecutionContext toolExecutionContext,
                             ToolEmitter toolEmitter
) throws InterruptedException {
    System.out.println("userContext: " + userContext.name());
    System.out.println("agent: " + agent);
    System.out.println("toolExecutionContext: " + toolExecutionContext.getStores().get(0));
    System.out.println("toolEmitter: " + toolEmitter);
    return LocalDateTime.now().toString();
}
java 复制代码
public record UserContext(String name){}

    @Bean
    public Agent reactAgent() {
        Toolkit toolkit = new Toolkit(
//                ToolkitConfig.builder()
//                .executionConfig(ExecutionConfig.builder()
//                        .maxAttempts(2)
//                        .timeout(Duration.ofSeconds(1))
//                        .build())
//                .build()
        );
        toolkit.registerTool(new DateTools());
//        ExecutionConfig executionConfig = ExecutionConfig.builder()
//                .timeout(Duration.ofSeconds(2))
////                .maxAttempts(3)
//                .build();
        ToolExecutionContext toolExecutionContext = ToolExecutionContext.builder()
                .register(new UserContext("赵六"))
                .register("name", String.class, "张三")
                .build();
        return ReActAgent.builder()
                .name("Hello Agent")
                // studio 可观测这个agent
                .hook(new StudioMessageHook(StudioManager.getClient()))
                .model(ollamaChatModel())
                .toolkit(toolkit)
//                .maxIters(2)
                // 优先模型配置
//                .toolExecutionConfig(executionConfig)
                .toolExecutionContext(toolExecutionContext)
                .build();
    }

2.1.4、预设参数

不需要大模型传递参数,而是可以手动传递参数

java 复制代码
public class WeatherTools {
    @Tool(name = "getWeather", description = "获取天气信息")
    public String getWeather(@ToolParam(name = "city", description = "城市") String city,
                             @ToolParam(name = "apiKey", description = "API Key") String apiKey) {
        return "晴空万里, city:" + city;
    }
}
java 复制代码
@Bean
public Agent reactAgent() {
    Toolkit toolkit = new Toolkit(
//                ToolkitConfig.builder()
//                .executionConfig(ExecutionConfig.builder()
//                        .maxAttempts(2)
//                        .timeout(Duration.ofSeconds(1))
//                        .build())
//                .build()
    );
    toolkit.registerTool(new DateTools());
    toolkit.registration()
            .tool(new WeatherTools())
            .presetParameters(Map.of("getWeather", Map.of("apiKey", "123456789")))
            .apply();

//        ExecutionConfig executionConfig = ExecutionConfig.builder()
//                .timeout(Duration.ofSeconds(2))
////                .maxAttempts(3)
//                .build();
    ToolExecutionContext toolExecutionContext = ToolExecutionContext.builder()
            .register(new UserContext("赵六"))
            .register("name", String.class, "张三")
            .build();
    return ReActAgent.builder()
            .name("Hello Agent")
            // studio 可观测这个agent
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(ollamaChatModel())
            .toolkit(toolkit)
//                .maxIters(2)
            // 优先模型配置
//                .toolExecutionConfig(executionConfig)
            .toolExecutionContext(toolExecutionContext)
            .build();
}

2.1.5、工具组

不需要给大模型全部的工具,而是自己注册一个工具组给大模型,让大模型按需激活然后调用

java 复制代码
@Bean
public DashScopeChatModel dashScopeChatModel() {
    return DashScopeChatModel.builder()
            .apiKey(System.getenv("DASHSCOPE_KEY"))
            .modelName("qwen-plus-2025-12-01")
//                .baseUrl("https://dashscope.aliyuncs.com/api/v1")
            .build();
}
@Bean
public Agent reactAgent() {
    Toolkit toolkit = new Toolkit();
    toolkit.registration()
            .tool(new DateTools())
            .apply();
    toolkit.registration()
            .tool(new WeatherTools())
            .presetParameters(Map.of("getWeather", Map.of("apiKey", "123456789")))
            .apply();

    toolkit.createToolGroup("date", "日期工具", false);
    toolkit.createToolGroup("weather", "天气工具", false);
    // 这种方式有问题, 不会自动激活, 建议使用.enableMetaTool(true)
//        toolkit.registerMetaTool();
    ToolExecutionContext toolExecutionContext = ToolExecutionContext.builder()
            .register(new UserContext("赵六"))
            .register("name", String.class, "张三")
            .build();
    return ReActAgent.builder()
            .name("Hello Agent")
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(dashScopeChatModel())
            .toolkit(toolkit)
            .enableMetaTool(true)
            .toolExecutionContext(toolExecutionContext)
            .build();
}

2.1.6、内置工具

java 复制代码
@Bean
    public Agent reactAgent() {
        Toolkit toolkit = new Toolkit();
        toolkit.registration()
                .tool(new DateTools())
                .apply();
        toolkit.registration()
                .tool(new WeatherTools())
                .presetParameters(Map.of("getWeather", Map.of("apiKey", "123456789")))
                .apply();

        toolkit.createToolGroup("date", "日期工具", false);
        toolkit.createToolGroup("weather", "天气工具", false);
        ToolExecutionContext toolExecutionContext = ToolExecutionContext.builder()
                .register(new UserContext("赵六"))
                .register("name", String.class, "张三")
                .build();

        toolkit.registerTool(new ReadFileTool("D:\\Desktop\\tmp"));
        toolkit.registerTool(new WriteFileTool("D:\\Desktop\\tmp"));
        // 默认允许执行所有的shell脚本
//        toolkit.registerTool(new ShellCommandTool());
        // 只允许执行ls、或者命令中包含python
        toolkit.registerTool(new ShellCommandTool(Set.of("ls"), cmd -> {
            if (cmd.contains("python")) {
                return true;
            }
            return false;
        }));

        return ReActAgent.builder()
                .name("Hello Agent")
                // studio 可观测这个agent
                .hook(new StudioMessageHook(StudioManager.getClient()))
//                .model(dashScopeChatModel())
                .model(ollamaChatModel())
                .toolkit(toolkit)
                .enableMetaTool(true)
                .toolExecutionContext(toolExecutionContext)
                .build();
    }

2.1.6、工具挂起

工具挂起,比如说需要调用外部系统的时候可以挂起,然后调用外部服务然后拿结果再给LLM

  1. 工具挂起
java 复制代码
package com.example.quickstart.tool;

import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.ToolSuspendException;

public class UserTools {
    @Tool(name = "getUserInfo", description = "获取用户信息")
    public String getUserInfo() {
        throw new ToolSuspendException("需要调用外部信息");
    }
}
  1. 注册工具
java 复制代码
@Bean
public Agent reactAgent() {
    Toolkit toolkit = new Toolkit(
    );

    toolkit.createToolGroup("date", "日期工具", false);
    toolkit.createToolGroup("weather", "天气工具", false);
    ToolExecutionContext toolExecutionContext = ToolExecutionContext.builder()
            .register(new UserContext("赵六"))
            .register("name", String.class, "张三")
            .build();


    toolkit.registerTool(new UserTools());
    return ReActAgent.builder()
            .name("Hello Agent")
            // studio 可观测这个agent
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(ollamaChatModel())
            .toolkit(toolkit)
            .enableMetaTool(true)
            .toolExecutionContext(toolExecutionContext)
            .build();
}
  1. 获取外部接口(测试)
java 复制代码
package com.example.quickstart.controller;

import io.agentscope.core.agent.Agent;
import io.agentscope.core.agent.Event;
import io.agentscope.core.message.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
public class HelloController {
    @Autowired
    private Agent agent;

    @GetMapping("/chat")
    public String chat(String question) {
        Msg block = agent.call(Msg.builder().textContent(question).build()).block();

        if (block.getGenerateReason() == GenerateReason.TOOL_SUSPENDED) {
            // 获取被挂起的工具调用信息
            ToolUseBlock toolUseBlock = block.getContentBlocks(ToolUseBlock.class).get(0);
            // 获取挂起的原因/提示信息
            ToolResultBlock toolResultBlock = block.getContentBlocks(ToolResultBlock.class).get(0);
            TextBlock contentBlock = (TextBlock) toolResultBlock.getOutput().get(0);
            String text = contentBlock.getText();
            if (text != null) {
                // 根据返回信息调用外部信息
                String userName = "张三, " + text;
                // 组装信息给大模型重新回复
                Msg result = Msg.builder()
                        .role(MsgRole.TOOL)
                        .content(ToolResultBlock.builder()
                                .id(toolUseBlock.getId())
                                .name(toolUseBlock.getName())
                                .output(TextBlock.builder().text(userName).build())
                                .build())
                        .build();
                block = agent.call(result).block();
            }
        }

        return block.getTextContent();
    }

}

2.2、循环次数

一次对话循环的次数

java 复制代码
@Bean
public Agent reactAgent() {
    Toolkit toolkit = new Toolkit();
    toolkit.registerTool(new DateTools());
    return ReActAgent.builder()
            .name("Hello Agent")
            // studio 可观测这个agent
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(ollamaChatModel())
            .toolkit(toolkit)
//                .maxIters(2)
            .build();
}

2.3、记忆

https://java.agentscope.io/v1/zh/docs/task/memory.html

2.3.1、短期记忆

2.3.1.1、InMemoryMemory

内存记忆,默认就是内存记忆

java 复制代码
 @Bean
public Agent reactAgent() {
    Toolkit toolkit = new Toolkit(
//                ToolkitConfig.builder()
//                .executionConfig(ExecutionConfig.builder()
//                        .maxAttempts(2)
//                        .timeout(Duration.ofSeconds(1))
//                        .build())
//                .build()
    );
//        toolkit.registerTool(new DateTools());
    toolkit.registration()
            .tool(new DateTools())
            .apply();
    toolkit.registration()
            .tool(new WeatherTools())
            .presetParameters(Map.of("getWeather", Map.of("apiKey", "123456789")))
            .apply();

//        ExecutionConfig executionConfig = ExecutionConfig.builder()
//                .timeout(Duration.ofSeconds(2))
////                .maxAttempts(3)
//                .build();
    toolkit.createToolGroup("date", "日期工具", false);
    toolkit.createToolGroup("weather", "天气工具", false);
    // 这种方式有问题, 不会自动激活, 建议使用.enableMetaTool(true)
//        toolkit.registerMetaTool();
    ToolExecutionContext toolExecutionContext = ToolExecutionContext.builder()
            .register(new UserContext("赵六"))
            .register("name", String.class, "张三")
            .build();

//        toolkit.registerTool(new ReadFileTool("D:\\Desktop\\tmp"));
//        toolkit.registerTool(new WriteFileTool("D:\\Desktop\\tmp"));
    // 默认允许执行所有的shell脚本
//        toolkit.registerTool(new ShellCommandTool());
    // 只允许执行ls、或者命令中包含python
//        toolkit.registerTool(new ShellCommandTool(Set.of("ls"), cmd -> {
//            if (cmd.contains("python")) {
//                return true;
//            }
//            return false;
//        }));

    toolkit.registerTool(new UserTools());
    return ReActAgent.builder()
            .name("Hello Agent")
            // studio 可观测这个agent
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(dashScopeChatModel())
//                .model(ollamaChatModel())
            .toolkit(toolkit)
            .enableMetaTool(true)
//                .maxIters(2)
            // 优先模型配置
//                .toolExecutionConfig(executionConfig)
            .toolExecutionContext(toolExecutionContext)
            // 默认也是开始内存短期记忆
            .memory(new InMemoryMemory())
            .build();
}
2.3.1.2、AutoContextMemroy
  1. 基本介绍
  1. 代码配置
java 复制代码
@Bean
public Agent reactAgent() {
    Toolkit toolkit = new Toolkit(
    );
    // toolkit.registerTool(new UserTools());
    AutoContextConfig autoContextConfig = AutoContextConfig.builder()
            .msgThreshold(50)
            .maxToken(64 * 1024)
            .tokenRatio(0.7)
            .lastKeep(20)
            .largePayloadThreshold(10 * 1024)
            .offloadSinglePreview(300)
            .minConsecutiveToolMessages(4)
            .currentRoundCompressionRatio(0.3)
            .build();
    AutoContextMemory autoContextMemory = new AutoContextMemory(autoContextConfig, dashScopeChatModel());

    return ReActAgent.builder()
            .name("Hello Agent")
            // studio 可观测这个agent
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(dashScopeChatModel())
            .toolkit(toolkit)
            .enableMetaTool(true)
            .toolExecutionContext(toolExecutionContext)
            // memroy、enablePlan、hook都要配置
            .memory(autoContextMemory)
            .enablePlan()
            .hook(new AutoContextHook())
            .build();
}

2.3.2、长期记忆

2.3.2.1、JsonSession

持久化到指定目录文件

java 复制代码
JsonSession session = new JsonSession(Path.of("D:\\Desktop\\tmp"));
agent.loadIfExists(session, "session_id_1");
// 执行agent调用
agent.saveTo(session, "session_id_1");
2.3.2.2、MysqlSession
  1. pom依赖(引入mysql及druid)
xml 复制代码
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-3-starter</artifactId>
    <version>1.2.28</version>
</dependency>
  1. yaml配置(连接mysql和druid配置)
yaml 复制代码
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/agentscope?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8&allowPublicKeyRetrieval=true
    username: root
    password: 123456
    druid:
      name: druid-db1
      # 初始化连接数
      initial-size: 5
      # 最小空闲连接数
      min-idle: 5
      # 最大活跃连接数
      max-active: 20
      # 获取连接最大等待时间(毫秒)
      max-wait: 60000
      # 检测间隔时间(毫秒),检测需要关闭的空闲连接
      time-between-eviction-runs-millis: 60000
      # 一个连接在池中最小生存的时间(毫秒)
      min-evictable-idle-time-millis: 300000
      # 一个连接在池中最大生存的时间(毫秒)
      max-evictable-idle-time-millis: 900000
      # 用来检测连接是否有效的 SQL
      validation-query: SELECT 1
      # 申请连接时执行 validationQuery 检测连接是否有效(默认 false,建议开启)
      test-while-idle: true
      # 申请连接时执行 validationQuery 检测连接是否有效(默认 true)
      test-on-borrow: false
      # 归还连接时执行 validationQuery 检测连接是否有效(默认 false)
      test-on-return: false
      # 打开 PSCache,并且指定每个连接上 PSCache 的大小
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      # 连接池中的 minIdle 数量以内的连接,空闲时间超过 minEvictableIdleTimeMillis,则会执行 keepAlive 操作
      keep-alive: true
      # 最大打开的预编译语句数
      max-open-prepared-statements: 20
      # 是否使用全局数据源统计,默认 false
      use-global-data-source-stat: true
      # 通过 connectProperties 属性来打开 mergeSql 功能;慢 SQL 记录
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

      # StatViewServlet 配置(监控页面)
      stat-view-servlet:
        enabled: true
        # 访问路径,默认是 /druid/*
        url-pattern: /druid/*
        # 允许访问的 IP 地址,空或 * 表示所有 IP
        allow: 127.0.0.1
        # 禁止访问的 IP 地址
        deny:
        # 登录用户名
        login-username: admin
        # 登录密码
        login-password: admin123
        # 是否可以重置数据源
        reset-enable: false

      # WebStatFilter 配置(Web 应用监控)
      web-stat-filter:
        enabled: true
        # 过滤 URL 规则
        url-pattern: /*
        # 排除一些静态资源
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
        # 监控 session 信息
        session-stat-enable: true
        # 设置单个 session 的最大并发数
        session-stat-max-count: 1000
        # 监控 principal 信息
        principal-session-name: user
        principal-cookie-name: user
        # 监控 profile 信息
        profile-enable: true

      # 内置 Filter 配置
      filter:
        # 状态监控过滤器
        stat:
          enabled: true
          # 数据库类型
          db-type: mysql
          # 开启慢 SQL 监控,超过指定时间则认为是慢 SQL,记录到日志中
          log-slow-sql: true
          # 慢 SQL 时间阈值(毫秒)
          slow-sql-millis: 5000

        # 日志监控过滤器,使用 logback 进行日志输出
        logback:
          enabled: true
          statement-log-error-enabled: true
          statement-create-after-log-enabled: false
          statement-close-after-log-enabled: false
          result-set-open-after-log-enabled: false
          result-set-close-after-log-enabled: false

        # 防火墙过滤器
        wall:
          enabled: true
          config:
            delete-allow: true        # 允许删除数据
            drop-table-allow: false    # 允许删除表
            alter-table-allow: true   # 允许修改表结构
            create-table-allow: true  # 允许创建表
            truncate-allow: true      # 允许清空表
            comment-allow: true       # 允许注释
            multi-statement-allow: true # 允许一次执行多条语句
            none-base-statement-allow: true # 允许非基础语句

          # 白名单配置(可选)
          white-list:
            - SELECT 1
            - SHOW VARIABLES LIKE 'version%'
  1. 测试写法
java 复制代码
@Autowired
private DataSource dataSource;

@GetMapping("/chat")
public String chat(String question) {
//        JsonSession session = new JsonSession(Path.of("D:\\Desktop\\tmp"));
    MysqlSession session = new MysqlSession(dataSource, true);
    agent.loadIfExists(session, "session_id_1");


    Msg block = agent.call(Msg.builder().textContent(question).build()).block();

    if (block.getGenerateReason() == GenerateReason.TOOL_SUSPENDED) {
        // 获取被挂起的工具调用信息
        ToolUseBlock toolUseBlock = block.getContentBlocks(ToolUseBlock.class).get(0);
        // 获取挂起的原因/提示信息
        ToolResultBlock toolResultBlock = block.getContentBlocks(ToolResultBlock.class).get(0);
        TextBlock contentBlock = (TextBlock) toolResultBlock.getOutput().get(0);
        String text = contentBlock.getText();
        if (text != null) {
            // 根据返回信息调用外部信息
            String userName = "张三, " + text;
            // 组装信息给大模型重新回复
            Msg result = Msg.builder()
                    .role(MsgRole.TOOL)
                    .content(ToolResultBlock.builder()
                            .id(toolUseBlock.getId())
                            .name(toolUseBlock.getName())
                            .output(TextBlock.builder().text(userName).build())
                            .build())
                    .build();
            block = agent.call(result).block();
        }
    }

    agent.saveTo(session, "session_id_1");

    return block.getTextContent();
}

2.4、结构化输出

  1. Agent配置
java 复制代码
// 两种模式,默认是Tool,还有Prompt
.structuredOutputReminder(StructuredOutputReminder.TOOL_CHOICE)
  1. 测试代码(重点关注call 和 return)
java 复制代码
 @GetMapping("/chat")
public String chat(String question) {
//        JsonSession session = new JsonSession(Path.of("D:\\Desktop\\tmp"));
    MysqlSession session = new MysqlSession(dataSource, true);
    agent.loadIfExists(session, "session_id_1");


    Msg block = agent.call(Msg.builder().textContent(question).build(), Movie.class).block();

    if (block.getGenerateReason() == GenerateReason.TOOL_SUSPENDED) {
        // 获取被挂起的工具调用信息
        ToolUseBlock toolUseBlock = block.getContentBlocks(ToolUseBlock.class).get(0);
        // 获取挂起的原因/提示信息
        ToolResultBlock toolResultBlock = block.getContentBlocks(ToolResultBlock.class).get(0);
        TextBlock contentBlock = (TextBlock) toolResultBlock.getOutput().get(0);
        String text = contentBlock.getText();
        if (text != null) {
            // 根据返回信息调用外部信息
            String userName = "张三, " + text;
            // 组装信息给大模型重新回复
            Msg result = Msg.builder()
                    .role(MsgRole.TOOL)
                    .content(ToolResultBlock.builder()
                            .id(toolUseBlock.getId())
                            .name(toolUseBlock.getName())
                            .output(TextBlock.builder().text(userName).build())
                            .build())
                    .build();
            block = agent.call(result).block();
        }
    }

    agent.saveTo(session, "session_id_1");

    return block.getStructuredData(Movie.class).toString();
}

2.5、Plan

https://java.agentscope.io/v1/zh/docs/task/plan.html

帮助智能体将复杂任务分解为结构化的子任务并逐步执行

2.5.1、使用默认配置(推荐)

plain 复制代码
ReActAgent agent = ReActAgent.builder()
        .name("Assistant")
        .model(model)
        .toolkit(toolkit)
        .enablePlan()  // 启用计划功能
        .build();

2.5.2、自定义配置

plain 复制代码
PlanNotebook planNotebook = PlanNotebook.builder()
        .maxSubtasks(10)  // 限制子任务数量
        .build();

ReActAgent agent = ReActAgent.builder()
        .name("Assistant")
        .model(model)
        .toolkit(toolkit)
        .planNotebook(planNotebook)
        .build();
java 复制代码
@Bean
    public ReActAgent reactAgent() {
        return ReActAgent.builder()
                .name("Hello Agent")
                // 默认配置
                .enablePlan()
                // 自定义配置
//                .planNotebook(PlanNotebook.builder()
//                        .maxSubtasks(10)
//                        .storage(new InMemoryPlanStorage())
//                        .planToHint(new DefaultPlanToHint())
//                        .build())
                .build();
    }

2.7、MCP

AgentScope Java 提供对 MCP (Model Context Protocol) 的完整支持,使智能体能够连接到外部工具服务器并使用 MCP 生态系统中的工具。

什么是 MCP?

MCP 是用于将 AI 应用程序连接到外部数据源和工具的标准协议。它支持:

  • 统一的工具接口:通过单个协议访问各种工具
  • 外部工具服务器:连接到专门的服务(文件系统、git、数据库等)
  • 生态系统集成:使用不断增长的 MCP 生态系统中的工具
  • 灵活的传输:支持 StdIO、SSE 和 HTTP 传输

传输类型

AgentScope 支持三种 MCP 传输机制:

传输 使用场景 连接方式 状态
StdIO 本地进程通信,比如操作文件、git、自定义命令 启动子进程 有状态
SSE HTTP Server-Sent Events HTTP 流式 有状态
HTTP 可流式 HTTP,比如调用外部的MCP工具 请求/响应 无状态

2.7.1、StdIO方式

  1. 连接到 MCP 服务器
plain 复制代码
import io.agentscope.core.tool.mcp.McpClientBuilder;
import io.agentscope.core.tool.mcp.McpClientWrapper;

// StdIO 传输 - 连接到本地 MCP 服务器
McpClientWrapper mcpClient = McpClientBuilder.create("filesystem-mcp")
        .stdioTransport("npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp")
        .buildAsync()
        .block();
  1. 注册 MCP 工具
plain 复制代码
import io.agentscope.core.tool.Toolkit;

Toolkit toolkit = new Toolkit();

// 注册 MCP 服务器的所有工具
toolkit.registerMcpClient(mcpClient).block();
  1. 在智能体中配置 MCP
plain 复制代码
import io.agentscope.core.ReActAgent;
import io.agentscope.core.memory.InMemoryMemory;

ReActAgent agent = ReActAgent.builder()
        .name("Assistant")
        .model(model)
        .toolkit(toolkit)  // MCP 工具现已可用
        .memory(new InMemoryMemory())
        .build();

StdIO 传输用于本地进程通信:

plain 复制代码
// 文件系统服务器
McpClientWrapper fsClient = McpClientBuilder.create("fs-mcp")
        .stdioTransport("npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir")
        .buildAsync()
        .block();

// Git 服务器
McpClientWrapper gitClient = McpClientBuilder.create("git-mcp")
        .stdioTransport("python", "-m", "mcp_server_git")
        .buildAsync()
        .block();

// 自定义命令
McpClientWrapper customClient = McpClientBuilder.create("custom-mcp")
        .stdioTransport("/path/to/executable", "arg1", "arg2")
        .buildAsync()
        .block();

2.7.2、HTTP方式

java 复制代码
McpClientWrapper httpClient = McpClientBuilder.create("http-mcp")
        .streamableHttpTransport("https://mcp.example.com/http")
        .header("X-API-Key", apiKey)
        .queryParam("queryKey", "queryValue")
        .buildAsync()
        .block();

2.8、Skill

  1. 可以去魔塔广场下载个skill放到resourse中
  1. 接入ReactAgent
java 复制代码
SkillBox skillBox = new SkillBox(toolkit);
skillBox.codeExecution()
        .workDir("D:\\Desktop\\tmp")
        .withShell(new ShellCommandTool())
        .withRead()
        .withWrite()
        .enable();

try (ClasspathSkillRepository repository = new ClasspathSkillRepository("skills")) {
    List<AgentSkill> allSkills = repository.getAllSkills();
    for (AgentSkill skill : allSkills) {
        skillBox.registerSkill(skill);
    }
} catch (IOException e) {
    throw new RuntimeException(e);
}

.skillBox(skillBox)

2.9、Hook

Hook 是一系列的扩展点,用于在特定执行阶段监控和修改智能体行为。

AgentScope Java 使用统一事件模型 ,所有 Hook 都需要实现 <font style="color:rgb(0, 0, 0);">onEvent(HookEvent)</font> 方法:

  • 基于事件:所有智能体活动生成事件
  • 类型安全:对事件类型进行模式匹配
  • 优先级排序:钩子按优先级执行(值越小优先级越高)
  • 可修改:某些事件允许修改执行上下文

支持的事件

事件类型 时机 可修改 描述
PreCallEvent 智能体调用前 ✅ 智能体开始处理之前(可修改输入消息)
PostCallEvent 智能体调用后 ✅ 智能体完成响应之后(可修改最终消息)
PreReasoningEvent 推理前 ✅ LLM 推理之前(可修改输入消息)
PostReasoningEvent 推理后 ✅ LLM 推理完成之后(可修改推理结果)
ReasoningChunkEvent 推理流式期间 ❌ 流式推理的每个块(仅通知)
PreActingEvent 工具执行前 ✅ 工具执行之前(可修改工具参数)
PostActingEvent 工具执行后 ✅ 工具执行之后(可修改工具结果)
ActingChunkEvent 工具流式期间 ❌ 工具执行进度块(仅通知)
PreSummaryEvent 摘要生成前 ✅ 达到最大迭代次数时,摘要生成之前
PostSummaryEvent 摘要生成后 ✅ 摘要生成完成之后(可修改摘要结果)
SummaryChunkEvent 摘要流式期间 ❌ 摘要流式生成的每个块(仅通知)
ErrorEvent 发生错误时 ❌ 发生错误时(仅通知)
  1. hook定义
java 复制代码
package com.example.quickstart.hook;

import com.alibaba.fastjson2.JSON;
import io.agentscope.core.hook.*;
import io.agentscope.core.message.Msg;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

public class LoggingHook implements Hook {
    @Override
    public <T extends HookEvent> Mono<T> onEvent(T event) {
        if (event instanceof PreCallEvent e) {
            // 参数传递
            List<Msg> inputMessages = e.getInputMessages();
            Msg msg = inputMessages.get(0);
            Map<String, Object> metadata = msg.getMetadata();
            System.out.println("元数据:" + metadata);

            System.out.println("智能体开始执行:" + e.getAgent().getName());
            System.out.println("PreCallEvent: "  + JSON.toJSONString(inputMessages));
        }

        if(event instanceof PostCallEvent e) {
            System.out.println("PostActingEvent: " + JSON.toJSONString(e.getFinalMessage()));
            System.out.println("智能体执行完毕:" + e.getAgent().getName());
        }

        return Mono.just(event);
    }
}
  1. 整合ReactAgent
java 复制代码
.hook(new LoggingHook())
  1. 测试代码(参数传递)
java 复制代码
Msg block = agent.call(Msg.builder().textContent(question)
        .metadata(Map.of("userId", "1"))
                .build(), Movie.class)
        .block();

2.10、RAG

可以理解为外链一个知识库

AgentScope 提供内置 RAG 支持,使 Agent 能够访问外部知识库。

2.10.1、核心组件

AgentScope 中的 RAG 模块由两个核心组件组成:

  • Reader(读取器):负责读取和分块输入文档,将其转换为可处理的单元
  • Knowledge(知识库):负责存储文档、生成嵌入向量以及检索相关信息

2.10.2、支持范围

AgentScope 支持多种类型的知识库实现:

类型 实现 支持功能 文档管理 适用场景
本地知识库 <font style="color:rgb(0, 0, 0);">SimpleKnowledge</font> 完整的文档管理和检索 通过代码管理(使用 Reader) 开发、测试、完全控制数据
云托管知识库 <font style="color:rgb(0, 0, 0);">BailianKnowledge</font> 仅检索 百炼控制台 企业级、多轮对话、查询重写
Dify 知识库 <font style="color:rgb(0, 0, 0);">DifyKnowledge</font> 仅检索 Dify 控制台 多种检索模式、Reranking
RAGFlow 知识库 <font style="color:rgb(0, 0, 0);">RAGFlowKnowledge</font> 仅检索 RAGFlow 控制台 强大OCR、知识图谱、多数据集

2.10.3、集成模式

AgentScope 支持两种 RAG 集成模式:

模式 描述 优点 缺点
Generic 模式 在每个推理步骤之前自动检索和注入知识 简单,适用于任何 LLM 即使不需要也会检索
Agentic 模式 Agent 使用工具决定何时检索 灵活,只在需要时检索 需要强大的推理能力
Generic 模式

在 Generic 模式下,知识会自动检索并注入到用户的消息中:

plain 复制代码
ReActAgent agent = ReActAgent.builder()
    .name("助手")
    .sysPrompt("你是一个可以访问知识库的有用助手。")
    .model(chatModel)
    .toolkit(new Toolkit())
    // 启用 Generic RAG 模式
    .knowledge(knowledge)
    .ragMode(RAGMode.GENERIC)
    .retrieveConfig(
        RetrieveConfig.builder()
            .limit(3)
            .scoreThreshold(0.3)
            .build())
    .build();

工作原理:

  1. 用户发送查询
  2. 知识库自动检索相关文档
  3. 检索到的文档被添加到用户消息之前
  4. Agent 处理增强后的消息并响应
Agentic 模式

在 Agentic 模式下,Agent 拥有 <font style="color:rgb(0, 0, 0);">retrieve_knowledge</font> 工具并决定何时使用它:

plain 复制代码
ReActAgent agent = ReActAgent.builder()
    .name("智能体")
    .sysPrompt("你是一个拥有知识检索工具的有用助手。" +
               "需要信息时使用 retrieve_knowledge 工具。")
    .model(chatModel)
    .toolkit(new Toolkit())
    // 启用 Agentic RAG 模式
    .knowledge(knowledge)
    .ragMode(RAGMode.AGENTIC)
    .retrieveConfig(
        RetrieveConfig.builder()
            .limit(3)
            .scoreThreshold(0.5)
            .build())
    .build();

工作原理:

  1. 用户发送查询
  2. Agent 推理并决定是否检索知识
  3. 如果需要,Agent 调用 <font style="color:rgb(0, 0, 0);">retrieve_knowledge(query="...")</font>
  4. 检索到的文档作为工具结果返回
  5. Agent 使用检索到的信息再次推理

本地知识库:

java 复制代码
@Bean
public ReActAgent reactAgent() {
    
    OllamaTextEmbedding embedding = OllamaTextEmbedding.builder()
            .modelName("bge-m3:567m")
            .dimensions(1024)
            .build();

    SimpleKnowledge knowledge = SimpleKnowledge.builder()
            .embeddingModel(embedding)
            .embeddingStore(InMemoryStore.builder().dimensions(1024).build())
            .build();
    TextReader textReader = new TextReader(512, SplitStrategy.PARAGRAPH, 50);
    List<Document> documents = textReader.read(ReaderInput.fromString("我的名字是田爷, 爱好看书, 打篮球")).block();
    knowledge.addDocuments(documents).block();


    return ReActAgent.builder()
            .name("Hello Agent")
            // studio 可观测这个agent
            .hook(new StudioMessageHook(StudioManager.getClient()))
            .model(dashScopeChatModel())
            .enableMetaTool(true)
            .enablePlan()
            .knowledge(knowledge)
//                .ragMode(RAGMode.GENERIC)
            // 如果使用 AGENTIC 模式则需要加系统提示词
            .sysPrompt("需要查询用户信息的时候使用 retrieve_knowledge 工具")
            .ragMode(RAGMode.AGENTIC)
            .retrieveConfig(RetrieveConfig.builder()
                    .limit(5)
                    .scoreThreshold(0.6)
                    .build())
            .build();
}

2.11、Human-in-the-Loop

Human-in-the-Loop 让你可以在智能体执行过程中插入人工审核环节。当智能体准备调用工具时,你可以先暂停让用户确认,再决定是否继续。

两个暂停时机

智能体的执行分为"推理"和"行动"两个阶段,你可以选择在不同时机暂停:

推理后暂停:模型决定要调用哪些工具后,在实际执行前暂停。此时你可以看到工具名称和参数,让用户决定是否允许执行。

行动后暂停:工具执行完毕后,在进入下一轮推理前暂停。此时你可以看到执行结果,让用户决定是否继续。

  1. 敏感词hook
java 复制代码
package com.example.quickstart.hook;

import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import io.agentscope.core.hook.PostReasoningEvent;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.TextBlock;
import reactor.core.publisher.Mono;

public class SensitiveWordHook implements Hook {
    @Override
    public <T extends HookEvent> Mono<T> onEvent(T event) {
        // 拦截推理结束 事件
        if (event instanceof PostReasoningEvent e) {
            // 获取推理结果
            Msg reasoningMessage = e.getReasoningMessage();
            // 如果推理结果为空或不包含文本
            if (reasoningMessage == null || !reasoningMessage.hasContentBlocks(TextBlock.class)) {
                return Mono.just(event);
            }
            String text = reasoningMessage.getTextContent();
            if (text != null && text.contains("大帅哥")) {
                e.stopAgent();
            }
        }

        return Mono.just(event);
    }
}
  1. ReactAgent添加hook
java 复制代码
.hook(new SensitiveWordHook())
  1. 测试代码拦截推理原因中断
java 复制代码
 else if (block.getGenerateReason() == GenerateReason.REASONING_STOP_REQUESTED) {
            block = agent.call(Msg.builder().textContent("内容包含敏感词了! ").build()).block();
        }