Java开发者的大模型入门:AgentScope Java组件全攻略(二)

九、AgentScope Runtime:部署与沙箱执行

在前面的章节中,我们一直在本地开发和测试智能体,通过 Spring Boot 的 Controller 暴露 HTTP 接口供外部调用。这种方式对于开发和演示足够了,但当我们想把智能体投入生产环境,面对真实用户的高并发请求时,就会遇到新的挑战:如何保证服务稳定、高效?如何防止恶意用户通过工具调用破坏系统?

AgentScope Runtime 正是为解决这些问题而生的。它是一个专为智能体设计的全栈运行时环境,核心目标是高效部署与服务 以及安全的沙箱执行。本章将带你学习如何使用 AgentScope Runtime 将智能体部署为可靠的服务,并配置沙箱来保护你的系统。

9.1 为什么需要专门的运行时?

9.1.1 部署挑战

想象一下,你的智能体被集成到电商网站的客服系统中,每天有成千上万用户访问。这时你会面临:

  • 高并发:多个请求同时到达,你的智能体需要能并行处理,不能阻塞。
  • 资源管理:每个请求都会调用大模型 API,如何控制并发度,避免超出 API 限流或导致内存溢出?
  • 稳定性:某个请求如果导致智能体死循环(例如 ReAct 无限循环),不能影响其他请求。

9.1.2 安全挑战

智能体的一大优势是能调用工具执行操作。但工具是把双刃剑------如果被恶意利用,可能造成严重破坏。例如:

  • 一个文件读取工具,如果被传入 ../../etc/passwd,就可能读取系统敏感文件。
  • 一个命令执行工具,如果被传入 rm -rf /,后果不堪设想。

因此,工具必须在受控的环境中执行,这就是**沙箱(Sandbox)**的作用。

9.1.3 AgentScope Runtime 的解决方案

AgentScope Runtime 提供了:

  • 高效服务:内置请求队列、线程池、负载均衡,支持高并发。
  • 智能体管理:可以同时部署多个智能体,通过名称路由。
  • 安全沙箱:限制工具执行的权限,例如只能访问特定目录、禁止执行系统命令、限制网络请求等。
  • 可观测性:输出指标(QPS、延迟、错误率)供监控系统(如下一章的 Studio)收集。

9.2 将智能体发布为 REST 服务

其实,我们在前面章节中通过 Spring Boot 和 @RestController 已经实现了智能体的 HTTP 服务。AgentScope Runtime 并没有推翻这种方式,而是在此基础上提供了更标准化的部署能力。通常,你仍然可以使用 Spring Boot 作为基础,然后集成 Runtime 的沙箱和监控功能。

9.2.1 回顾:最简单的 HTTP 服务

假设我们有一个智能体 WeatherAgent,我们通过 Controller 暴露:

java 复制代码
@RestController
public class AgentController {

    @Autowired
    private WeatherAgent weatherAgent;

    @PostMapping("/agent/weather")
    public String chat(@RequestBody String query) {
        return weatherAgent.run(query);
    }
}

启动 Spring Boot 应用后,可以通过 HTTP POST 请求调用。

9.2.2 使用 AgentScope Runtime 管理智能体

AgentScope Runtime 提供了一个 AgentServer 类,可以统一管理多个智能体,并提供统一的 REST API。我们可以在 Spring Boot 启动时初始化它。

首先,引入 Runtime 依赖(假设已经包含在 agentscope-spring-boot-starter 中,但可能需要单独添加)。如果 starter 已包含则无需操作。

然后,创建一个配置类,注册智能体到 Runtime:

java 复制代码
@Configuration
public class RuntimeConfig {

    @Bean
    public AgentServer agentServer(List<Agent> agents) {
        AgentServer server = new AgentServer();
        // 将所有 Spring 容器中的 Agent 注册到服务器
        for (Agent agent : agents) {
            server.registerAgent(agent.getName(), agent);
        }
        server.start(); // 启动服务器(非阻塞)
        return server;
    }
}

AgentServer 会启动一个内置的 HTTP 服务器(比如基于 Netty),提供统一入口:http://host:port/api/agent/{agentName}。这样,我们可以通过这个统一入口调用任何注册的智能体,而不需要为每个智能体编写 Controller。

9.2.3 配置文件定义智能体

除了代码注册,Runtime 还支持通过配置文件定义智能体。在 application.yml 中添加:

yaml 复制代码
agentscope:
  runtime:
    agents:
      - name: weather_agent
        class: com.example.demo.agent.WeatherAgent
        max-concurrent: 10
      - name: travel_agent
        class: com.example.demo.agent.TravelAgent
        max-concurrent: 5

Runtime 会自动加载这些类并创建智能体实例,同时限制每个智能体的最大并发请求数。

9.3 沙箱执行机制

9.3.1 为什么需要沙箱?

沙箱是一种安全机制,它将工具的执行环境隔离在一个受限的"盒子"里,限制其对系统资源的访问。例如,我们可以允许工具读取 /data 目录下的文件,但不允许访问 /etc;允许进行 HTTP 请求,但不允许连接到内网 IP。

9.3.2 AgentScope 沙箱的工作原理

AgentScope 的沙箱通过 Java 的 SecurityManager 或自定义的类加载器、权限检查来实现。当工具方法被调用时,Runtime 会检查当前执行上下文是否在沙箱内,并应用预先配置的权限策略。

简化来说,沙箱会在调用工具方法之前,设置一个安全上下文,拦截危险操作。

9.3.3 配置沙箱权限

我们可以在 application.yml 中为每个工具或全局配置沙箱规则:

yaml 复制代码
agentscope:
  runtime:
    sandbox:
      enabled: true
      default-policy: deny  # 默认禁止所有
      rules:
        - tools: ["readFile"]  # 对 readFile 工具生效
          file:
            allowed-paths: ["/data", "/tmp"]
            read-only: true
        - tools: ["executeCommand"]
          enabled: false  # 完全禁止 executeCommand 工具
        - global:  # 全局规则
          network:
            allowed-hosts: ["api.weather.com", "api.example.com"]
            allow-private-ips: false
          filesystem:
            allowed-paths: ["/app/data"]

这些规则将在工具执行前进行校验,如果违反规则,工具调用会失败并抛出安全异常。

9.3.4 在工具代码中感知沙箱

工具开发者通常不需要关心沙箱,沙箱自动生效。但有时工具需要根据权限调整行为(例如,如果只允许读,则不能写)。可以通过 SandboxContext 获取当前权限信息,但这不是必须的。

9.4 实践:部署一个带沙箱的智能体服务

让我们通过一个例子来体验沙箱的作用。我们将创建一个带有"文件读取"工具的智能体,然后配置沙箱限制它只能读取特定目录。

9.4.1 定义文件读取工具

java 复制代码
@Component
public class FileTool {

    @Tool(name = "readFile", description = "读取指定路径的文件内容")
    public String readFile(@Param(description = "文件路径") String path) throws IOException {
        // 注意:这里没有安全检查,沙箱会拦截
        return Files.readString(Path.of(path));
    }
}

9.4.2 创建智能体

java 复制代码
@Component
public class FileAgent extends BaseAgent {
    public FileAgent(ChatClient chatClient) {
        super("file_agent", chatClient);
    }

    @Override
    protected String getSystemPrompt() {
        return "你是一个文件助手,可以使用 readFile 工具读取文件。";
    }
}

9.4.3 配置沙箱规则

application.yml 中启用沙箱,并设置规则:

yaml 复制代码
agentscope:
  runtime:
    sandbox:
      enabled: true
      default-policy: deny
      rules:
        - tools: ["readFile"]
          file:
            allowed-paths: ["/tmp", "./data"]  # 只允许读取 /tmp 和 ./data 目录
            read-only: true

9.4.4 注册智能体到 Runtime

使用前面的 AgentServer 配置或通过配置文件注册。

9.4.5 测试

  1. /tmp 目录下创建一个测试文件 test.txt,内容为 "Hello Sandbox"。
  2. 在项目根目录下创建 data/test.txt,内容为 "Project Data"。
  3. 启动应用。
  4. 调用智能体,让它读取 /tmp/test.txt
bash 复制代码
curl -X POST http://localhost:8080/api/agent/file_agent \
  -H "Content-Type: application/json" \
  -d '请帮我读取 /tmp/test.txt 的内容'

应该能成功返回内容。

  1. 尝试读取系统敏感文件,比如 /etc/passwd
bash 复制代码
curl -X POST http://localhost:8080/api/agent/file_agent \
  -H "Content-Type: application/json" \
  -d '请帮我读取 /etc/passwd'

这时,沙箱会拦截,智能体可能返回类似"权限不足,无法读取该文件"的错误信息。

9.4.6 沙箱工作流程

sequenceDiagram participant 用户 participant 智能体 participant 沙箱 participant 工具 用户->>智能体: 读取 /etc/passwd 智能体->>智能体: 决定调用 readFile 工具 智能体->>沙箱: 请求执行 readFile("/etc/passwd") 沙箱->>沙箱: 检查权限规则 沙箱-->>智能体: 拒绝执行(抛出异常) 智能体-->>用户: 返回"权限不足"

如果路径允许,沙箱放行,工具正常执行。

9.5 可观测性(Runtime 层面)

AgentScope Runtime 内置了指标收集功能,可以记录每个智能体的请求数、平均耗时、错误数等。这些指标可以通过 JMX 或 HTTP 端点暴露,供 Prometheus 等监控系统抓取。

application.yml 中开启:

yaml 复制代码
agentscope:
  runtime:
    metrics:
      enabled: true
      endpoint: /metrics  # 暴露指标的 HTTP 路径

访问 http://localhost:8080/metrics 可以看到类似:

ini 复制代码
# HELP agentscope_requests_total Total requests per agent
# TYPE agentscope_requests_total counter
agentscope_requests_total{agent="file_agent",} 10.0
# HELP agentscope_request_duration_seconds Request duration per agent
# TYPE agentscope_request_duration_seconds histogram
agentscope_request_duration_seconds_bucket{agent="file_agent",le="0.1",} 5.0
...

这些指标可以与下一章的 AgentScope Studio 集成,实现可视化监控。

9.6 本章小结

通过本章的学习,你掌握了:

  • AgentScope Runtime 的核心作用:高效部署 + 安全沙箱。
  • 将智能体发布为服务 :使用 Runtime 的 AgentServer 统一管理智能体,通过配置定义并发限制。
  • 沙箱机制:为什么需要、如何配置权限规则,以及如何在工具执行时自动应用。
  • 实践:部署了一个带文件读取工具的智能体,验证沙箱的访问限制。
  • 可观测性:了解 Runtime 提供的指标端点,为监控做准备。

十、AgentScope Studio------可观测性与调试

在上一章中,我们学习了如何将智能体安全高效地部署为服务。但当智能体真正运行在生产环境中,我们如何了解它的内部状态?如何查看它每一步的思考过程?如果回答出错,如何追溯原因?可观测性 正是解决这些问题的关键,而 AgentScope Studio 提供了强大的可视化工具,让你能够像调试代码一样调试智能体。

本章将带你掌握 AgentScope Studio 的使用,包括集成方式、查看执行轨迹、评估智能体性能等。

10.1 为什么需要 Studio?

想象一下,你部署了一个复杂的 ReAct 智能体,它调用多个工具,经历多轮思考循环。某一天,用户投诉它给出了错误的答案。如果没有可观测性工具,你只能查看日志,面对大量混杂的文本,很难还原智能体当时的决策过程。

AgentScope Studio 可以:

  • 可视化执行轨迹:以图形化的方式展示智能体的思考步骤、工具调用和观察结果。
  • 实时调试:在开发环境中,你可以与智能体交互并实时查看它的思考链条。
  • 评估智能体:通过测试集批量运行智能体,生成准确率、相关性等指标报告。
  • 管理提示词:与 Nacos 集成,动态调整提示词并观察效果(需要结合配置中心)。

10.2 集成 Studio 到项目

AgentScope Studio 可以以两种方式使用:

  • 嵌入模式:作为 Spring Boot 应用的一部分,在本地启动一个 Web 界面。
  • 独立模式:部署独立的 Studio 服务,连接到你的智能体服务。

我们主要介绍嵌入模式,因为它简单易用,适合开发和测试。

10.2.1 引入依赖

pom.xml 中添加 Studio 依赖(如果尚未包含在 starter 中):

xml 复制代码
<dependency>
    <groupId>com.alibaba.agentscope</groupId>
    <artifactId>agentscope-studio-spring-boot-starter</artifactId>
    <version>${agentscope.version}</version>
</dependency>

10.2.2 配置文件

application.yml 中开启 Studio 并配置访问路径:

yaml 复制代码
agentscope:
  studio:
    enabled: true
    path: /studio  # Studio UI 的访问路径
    # 其他配置,如数据存储等(可选)

10.2.3 启动应用

启动 Spring Boot 应用后,访问 http://localhost:8080/studio,你将看到 Studio 的主界面。如果没有看到,请检查控制台日志,确认 Studio 是否正确初始化。

10.3 查看智能体执行轨迹

Studio 的核心功能之一是可视化智能体的执行过程。让我们通过一个示例来体验。

10.3.1 准备一个 ReAct 智能体

复用第七章的代码助手智能体(包含 executeCode 工具)。确保它已经注册为 Spring Bean。

10.3.2 通过 Studio 发起对话

在 Studio 界面左侧,你可以选择一个智能体(如 code_assistant),然后在输入框中输入问题,例如:"请写一个 Python 函数计算斐波那契数列的第 10 项并运行它"。点击发送。

Studio 会在右侧显示执行过程,可能包括:

  • 思考:智能体每一步的推理文字。
  • 工具调用:显示调用了哪个工具,参数是什么。
  • 工具结果:工具返回的内容。
  • 最终回答:最终的输出。

这些信息以树形结构或时间线呈现,非常直观。

10.3.3 执行轨迹的 Mermaid 可视化

Studio 内部可以将执行轨迹导出为 Mermaid 格式,方便嵌入文档或分享。一个典型的 ReAct 循环的 Mermaid 图如下:

graph TD A[用户输入: 写代码计算斐波那契] --> B[思考1:需要编写代码] B --> C[行动1:生成代码] C --> D[工具:executeCode] D --> E[观察1:代码执行出错] E --> F[思考2:分析错误,修改代码] F --> G[行动2:生成修改后的代码] G --> H[工具:executeCode] H --> I[观察2:执行成功] I --> J[最终回答:结果是55]

10.3.4 如何获取这些轨迹数据

Studio 通过监听智能体的事件来收集数据。ReActAgent 在执行过程中会发出事件,Studio 订阅这些事件并存储。如果你使用自定义智能体,可以通过继承 BaseAgent 并在关键步骤发送事件来集成,但通常 ReActAgent 已经内置了。

10.4 评估(Evaluation)功能

除了实时调试,Studio 还支持批量评估智能体。你可以定义一组测试用例(输入和预期输出),让智能体批量运行,然后生成评估报告。

10.4.1 创建测试集

在 Studio UI 中,你可以上传一个 JSON 文件作为测试集。格式如下:

json 复制代码
[
  {
    "id": "1",
    "input": "计算 123 + 456",
    "expected": "579"
  },
  {
    "id": "2",
    "input": "上海的天气",
    "expected": "多云,28℃"
  }
]

10.4.2 运行评估

选择要评估的智能体(如 tool_agent),选择测试集,点击开始评估。Studio 会依次运行每个测试用例,记录输出和预期结果的对比。

10.4.3 评估报告

评估完成后,Studio 会生成报告,包括:

  • 准确率:完全匹配的比例。
  • 耗时统计:平均/最大/最小响应时间。
  • 失败案例:列出不一致的用例,方便分析。

你还可以让智能体自己评估结果(LLM-as-a-judge),对比两个版本的智能体输出。

10.4.4 通过 Java API 触发评估(高级)

除了 UI,你也可以通过 Java API 编程方式触发评估。例如,在单元测试中:

java 复制代码
@Autowired
private EvaluationService evaluationService;

@Test
void evaluateAgent() {
    EvaluationRequest request = new EvaluationRequest();
    request.setAgentName("code_assistant");
    request.setTestSet(testSet);
    EvaluationReport report = evaluationService.evaluate(request);
    assertEquals(0.8, report.getAccuracy());
}

这需要引入 agentscope-evaluation 模块,但本文从简。

10.5 实践:使用 Studio 调试多智能体系统

让我们结合第五章的旅游规划多智能体系统,用 Studio 来观察它的协作过程。

10.5.1 确保所有智能体被 Studio 识别

只要智能体是 Spring Bean,Studio 会自动发现它们。在 Studio 界面左侧,你应该能看到 travel_supervisorweather_workerhotel_workeritinerary_worker 等。

10.5.2 发起一次规划请求

选择 travel_supervisor 作为入口智能体(或者直接调用它的 HTTP 接口,Studio 会自动捕获)。输入"我想去北京旅游3天"。

10.5.3 观察多智能体交互轨迹

Studio 可能会将这次调用展示为一个父流程,内部包含多个子流程(每个工人的执行)。你可以展开查看:

  • 主管发送给天气工人的消息。
  • 天气工人的内部 ReAct 循环(如果有工具)。
  • 工人返回结果给主管。
  • 最终汇总。

这让你能够清楚地看到整个团队是如何协作的。

10.5.4 可视化多智能体协作图

用 Mermaid 可以这样表示:

graph TD subgraph 用户请求 A[用户: 北京旅游3天] end A --> B[主管智能体] B --> C[发送天气任务] B --> D[发送酒店任务] B --> E[发送行程任务] C --> F[天气工人] F --> G[调用天气工具] G --> H[返回结果] D --> I[酒店工人] I --> J[调用酒店工具] J --> K[返回结果] E --> L[行程工人] L --> M[调用景点工具] M --> N[返回结果] H --> B K --> B N --> B B --> O[汇总结果] O --> P[返回用户]

10.6 本章小结

通过本章的学习,你掌握了:

  • AgentScope Studio 的作用:可视化调试、执行轨迹、评估功能。
  • 集成 Studio:通过 starter 快速集成,访问 UI 界面。
  • 查看执行轨迹:理解智能体的思考与行动过程。
  • 评估功能:批量测试智能体,生成质量报告。
  • 实践:用 Studio 调试多智能体协作系统,观察消息传递。

Studio 是你开发智能体的"眼睛",让复杂的智能体行为变得透明可解释。结合前一章的 Runtime,你已经拥有了一个完整的企业级智能体开发和运维平台。

十一、高级特性:多模态与 ReMe 记忆系统

在前面的章节中,我们构建的智能体都只处理文本信息。但现实世界的信息是多样的------图片、语音、视频......要让智能体真正像人一样感知世界,它必须具备多模态 能力。同时,之前的记忆(第六章)只是简单的短期对话历史,无法实现跨会话的个性化长期记忆。本章将带你探索 AgentScope Java 的这两项高级特性:多模态智能体ReMe 记忆系统,让你的智能体既能"看"又能"听",还能真正记住用户。

11.1 多模态智能体概述

11.1.1 什么是多模态?

多模态是指智能体能够处理多种类型的数据,包括文本、图像、音频、视频等。对于大模型来说,多模态意味着模型可以同时理解文本和图像(如 GPT-4V、通义千问 VL),甚至生成图像(如通义万相、DALL·E)。

在智能体应用中,多模态能力可以带来丰富的场景:

  • 图像理解:根据一张图片回答用户问题("这张图里有什么?"、"这个产品是什么型号?")。
  • 图像生成:根据用户描述生成图片("画一只穿着西服的猫")。
  • 图文联合推理:分析图表、识别场景等。

11.1.2 AgentScope Java 对多模态的支持

AgentScope Java 通过统一的 ChatClient 接口支持多模态模型的调用。只要底层模型支持图像输入(如通义千问 VL 系列),你就可以在消息中附加图片。对于图像生成,AgentScope 提供了专门的工具封装,也可以通过自定义工具集成通义万相。

能力 模型 AgentScope 支持方式
图像理解 qwen-vl-plus / qwen-vl-max UserMessage 中添加图片内容
图像生成 通义万相(wanx) 通过自定义工具或内置 ImageGenerationTool(如果存在)

11.2 图像理解:让智能体看懂图片

11.2.1 配置支持视觉的模型

application.yml 中配置通义千问 VL 模型:

yaml 复制代码
agentscope:
  llm:
    provider: dashscope
    api-key: ${DASHSCOPE_API_KEY}
    model: qwen-vl-plus  # 使用视觉模型

11.2.2 构造包含图片的消息

AgentScope 的 UserMessage 支持包含多模态内容。我们需要使用 MediaMessage 或扩展的消息类型。目前 AgentScope 可能使用 ChatMessage 的子类 MultiModalMessage。我们参考之前 Spring AI Alibaba 的方式,构造一个包含图片的消息。

假设 AgentScope 提供了 ImageContent 类,我们可以这样构建:

java 复制代码
import com.alibaba.agentscope.message.UserMessage;
import com.alibaba.agentscope.message.MediaContent;

// 从 URL 加载图片
UserMessage userMessage = UserMessage.builder()
        .text("请描述这张图片")
        .image("https://example.com/cat.jpg")  // 假设有 image 方法
        .build();

如果没有内置支持,我们可以通过提示词传递图片 URL,让模型自行获取(通义千问 VL 支持 URL)。但更推荐的是将图片转为 Base64 并嵌入消息。我们可以在工具中实现图片处理。

11.2.3 实践:构建图像描述智能体

创建一个专门的 VisionAgent,它能接收图片并回答相关问题。

java 复制代码
package com.example.demo.agent;

import com.alibaba.agentscope.agent.BaseAgent;
import com.alibaba.agentscope.llm.ChatClient;
import com.alibaba.agentscope.message.UserMessage;
import org.springframework.stereotype.Component;

@Component
public class VisionAgent extends BaseAgent {

    public VisionAgent(ChatClient chatClient) {
        super("vision_agent", chatClient);
    }

    @Override
    protected String getSystemPrompt() {
        return "你是一个视觉助手,可以根据图片回答用户的问题。";
    }

    // 提供一个专门处理图片的方法
    public String describeImage(String imageUrl, String question) {
        // 构造提示词,将图片 URL 包含在文本中
        String prompt = String.format("图片地址:%s\n问题:%s", imageUrl, question);
        return run(prompt);
    }

    // 如果支持多模态消息,可以用更直接的方式,这里简化
}

注意:由于通义千问 VL 模型可以直接接受图片 URL 作为输入的一部分,我们只需在提示词中包含 URL 即可。更规范的方式是使用多模态消息格式,但目前 AgentScope 可能还未完全封装,我们先用文本方式。

11.2.4 测试

在 Controller 中调用:

java 复制代码
@RestController
public class VisionController {

    @Autowired
    private VisionAgent visionAgent;

    @GetMapping("/describe")
    public String describe(@RequestParam String imageUrl, 
                           @RequestParam(defaultValue = "请描述这张图片") String question) {
        return visionAgent.describeImage(imageUrl, question);
    }
}

访问 http://localhost:8080/describe?imageUrl=https://example.com/cat.jpg&question=这是什么动物?,智能体应该能回答。

11.3 图像生成:让智能体画出图片

11.3.1 配置通义万相模型

通义万相是阿里云的文生图模型,需要通过单独的 API 调用。我们可以在 AgentScope 中创建一个工具来封装它。

首先,确保你有通义万相的 API 权限(通常与通义千问共用 API Key)。模型名称可能是 wanx-v1

11.3.2 创建图像生成工具

java 复制代码
package com.example.demo.tool;

import com.alibaba.agentscope.tool.annotation.Param;
import com.alibaba.agentscope.tool.annotation.Tool;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult;
import org.springframework.stereotype.Component;

@Component
public class ImageGenerationTool {

    @Tool(name = "generateImage", description = "根据文本描述生成一张图片")
    public String generateImage(
            @Param(description = "图片描述,例如'一只穿着西装的猫'") String prompt) {
        try {
            ImageSynthesisParam param = ImageSynthesisParam.builder()
                    .model("wanx-v1")
                    .prompt(prompt)
                    .n(1)
                    .size("1024*1024")
                    .build();

            ImageSynthesis imageSynthesis = new ImageSynthesis();
            ImageSynthesisResult result = imageSynthesis.call(param);
            // 返回第一张图片的 URL
            return result.getOutput().getResults().get(0).getImageUrl();
        } catch (Exception e) {
            e.printStackTrace();
            return "图片生成失败:" + e.getMessage();
        }
    }
}

注意:上述代码使用了阿里云 DashScope SDK,需要引入依赖:

xml 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dashscope-sdk-java</artifactId>
    <version>2.12.0</version>
</dependency>

11.3.3 创建图像生成智能体

java 复制代码
@Component
public class ImageGenAgent extends BaseAgent {

    public ImageGenAgent(ChatClient chatClient) {
        super("image_gen_agent", chatClient);
    }

    @Override
    protected String getSystemPrompt() {
        return "你是一个图像生成助手,可以根据用户描述生成图片。使用 generateImage 工具。";
    }
}

由于工具自动注册,ImageGenAgent 会自动拥有调用 generateImage 的能力。我们可以用 ReActAgent 或直接让 BaseAgent 调用工具?BaseAgent 默认不会主动调用工具,需要 ReActAgent 的循环。因此,我们应该使用 ReActAgent 来包装,或者将 ImageGenAgent 改为 ReActAgent

我们可以在配置中创建 ReActAgent Bean:

java 复制代码
@Bean
public ReActAgent imageGenAgent(ChatClient chatClient) {
    return ReActAgent.builder()
            .name("image_gen_agent")
            .chatClient(chatClient)
            .systemPrompt("你是一个图像生成助手,可以根据用户描述生成图片。使用 generateImage 工具。")
            .maxIterations(3)
            .build();
}

11.3.4 测试

创建控制器:

java 复制代码
@RestController
public class ImageGenController {

    @Autowired
    @Qualifier("imageGenAgent")
    private ReActAgent imageGenAgent;

    @GetMapping("/generate")
    public String generate(@RequestParam String prompt) {
        return imageGenAgent.run(prompt);
    }
}

访问 http://localhost:8080/generate?prompt=一只穿着西装的猫,智能体会返回生成的图片 URL。你可以在浏览器中打开该 URL 查看图片。

11.4 ReMe 记忆系统简介

11.4.1 为什么需要更复杂的记忆?

第六章我们实现的记忆只是简单的对话历史存储,无法区分长期和短期,也不能跨会话保留。真正的智能体需要两种记忆:

  • 短期工作记忆:当前对话的上下文,用于保持连贯对话。
  • 长期经验记忆:跨会话的用户偏好、习惯、重要事实,让智能体越来越了解用户。

ReMe 是 AgentScope 提出的记忆系统,它模拟人类的记忆机制,包含三个层次:

记忆层次 功能 示例
工作记忆 当前对话的短期记忆,类似 TokenWindowMemory 刚才用户说了什么
个人记忆 关于用户的信息,长期存储 用户喜欢 Java、家住上海
工具记忆 工具使用经验的累积 用户经常查询天气,可能偏好简洁回答

11.4.2 ReMe 的核心设计

ReMe 在 AgentScope 中的设计目标是:

  • 持久化:个人记忆和工具记忆存储在外部数据库中(如 Redis、关系库)。
  • 结构化:记忆以三元组形式存储(主体、关系、客体),便于检索。
  • 自动提取与整合:智能体在对话中自动提取重要信息存入长期记忆,并在需要时检索使用。

目前 AgentScope Java 中 ReMe 的实现可能还在演进中,但我们可以用 Redis 模拟个人记忆存储,让智能体具备简单的长期记忆能力。

11.5 实践:为智能体添加长期记忆

我们通过一个简单的例子展示如何让智能体记住用户的偏好(例如用户喜欢的编程语言)。

11.5.1 设计记忆存储

使用 Redis 存储用户偏好。我们定义一个 UserMemoryService,负责存取用户信息。

java 复制代码
@Service
public class UserMemoryService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String KEY_PREFIX = "user:pref:";

    public void savePreference(String userId, String key, String value) {
        String redisKey = KEY_PREFIX + userId + ":" + key;
        redisTemplate.opsForValue().set(redisKey, value);
    }

    public String getPreference(String userId, String key) {
        String redisKey = KEY_PREFIX + userId + ":" + key;
        return redisTemplate.opsForValue().get(redisKey);
    }
}

11.5.2 创建带记忆的智能体

智能体在每次对话时,从 Redis 加载用户偏好,并在对话中根据上下文更新偏好。这需要我们在智能体运行时能够访问用户 ID。

我们设计智能体的 run 方法接收两个参数:userId 和 message。但 BaseAgent.run 只接受一个字符串,所以我们需要自定义接口。

可以创建一个新的 Agent 接口:

java 复制代码
public interface PersonalAgent {
    String run(String userId, String message);
}

然后实现类:

java 复制代码
@Component
public class PersonalAgentImpl implements PersonalAgent {

    @Autowired
    private ChatClient chatClient;

    @Autowired
    private UserMemoryService memoryService;

    @Override
    public String run(String userId, String message) {
        // 1. 加载用户偏好
        String langPref = memoryService.getPreference(userId, "language");

        // 2. 构建系统提示,融入偏好
        String systemPrompt = "你是一个个性化助手。";
        if (langPref != null) {
            systemPrompt += "用户偏好编程语言:" + langPref;
        }

        // 3. 调用大模型
        // 这里简化,直接构造消息
        List<ChatMessage> messages = List.of(
            new SystemMessage(systemPrompt),
            new UserMessage(message)
        );
        String response = chatClient.chat(messages);

        // 4. 从对话中提取新的偏好(简单实现:如果用户说"我喜欢X语言",则存储)
        if (message.contains("我喜欢") && message.contains("语言")) {
            // 简单正则提取,实际应用中可以用 LLM 提取
            Pattern pattern = Pattern.compile("我喜欢(\\w+)语言");
            Matcher matcher = pattern.matcher(message);
            if (matcher.find()) {
                String lang = matcher.group(1);
                memoryService.savePreference(userId, "language", lang);
            }
        }

        return response;
    }
}

11.5.3 控制器

java 复制代码
@RestController
public class PersonalController {

    @Autowired
    private PersonalAgent personalAgent;

    @PostMapping("/personal-chat")
    public String chat(@RequestParam String userId, @RequestParam String message) {
        return personalAgent.run(userId, message);
    }
}

11.5.4 测试

  1. 第一次对话:/personal-chat?userId=123&message=我喜欢Java语言
  2. 智能体可能回答一些内容,同时存储了偏好。
  3. 第二次对话(同一用户):/personal-chat?userId=123&message=推荐一本编程书 智能体会在系统提示中包含"用户偏好Java语言",从而推荐 Java 相关书籍。

这虽然简单,但展示了长期记忆的基本思想。未来 ReMe 将提供更智能的自动提取和检索机制。

11.6 本章小结

通过本章的学习,你探索了 AgentScope Java 的高级特性:

  • 多模态图像理解:使用通义千问 VL 模型,让智能体能够描述图片内容。
  • 图像生成:通过工具封装通义万相,让智能体能够根据描述生成图片。
  • ReMe 记忆系统:了解了长期记忆的重要性,并通过 Redis 实现了简单的用户偏好记忆。

这些能力让智能体更加接近人类,既能"看"又能"画",还能真正记住用户,提供个性化服务。随着 AgentScope 的发展,多模态和记忆功能将更加强大和完善。

十二、总结与最佳实践

我们已经完整地走完了 AgentScope Java 的学习之旅。从最初的环境搭建到高级的多模态与记忆系统,亲手实践了智能体开发的全过程,构建了从简单聊天到复杂多智能体协作的各类应用。现在,是时候回顾所学、提炼经验,并为未来之路指明方向了。

12.1 AgentScope Java 组件全景回顾

在之前的十一章中,我们逐步探索了以下核心组件,每个组件都在智能体系统中扮演着独特的角色。下表为你提供一个清晰的快速索引:

类别 核心组件/概念 章节 核心作用 你的实践成果
基础 Agent, BaseAgent, ChatClient 二、三 定义智能体基座,统一模型调用 第一个 Hello Agent,Java导师助手
工具 @Tool, ToolManager 让智能体调用外部方法,获取实时信息 天气、时间、计算器工具
多智能体 MessageHub, 广播/点对点 实现智能体间的通信与协作 旅游规划多智能体系统
记忆 Memory, ConversationMemory, TokenWindowMemory 保持对话上下文,实现短期记忆 带记忆的聊天助手
ReAct ReActAgent, 思考-行动-观察循环 处理多步复杂任务,自我纠错 代码编写与调试助手
工作流 SequentialPipeline, ParallelPipeline, ConditionalPipeline 编排确定性业务流程 智能客服路由工作流
部署 AgentServer, 沙箱机制 安全高效地部署智能体服务 带沙箱的文件读取智能体
可观测 Studio, 执行轨迹, 评估 可视化调试与性能评估 使用Studio调试多智能体系统
高级 多模态(VL/图像生成), ReMe记忆 十一 拓展感知与长期个性化记忆 图像描述与生成、用户偏好记忆

一句话总结:AgentScope Java 提供了一套从基础到高级、从开发到部署的完整工具箱,让你能像搭积木一样构建智能体应用,并确保其可靠、安全、可观测。

12.2 生产环境选型建议

将智能体投入生产前,需要根据实际场景做出明智的决策。以下是一些关键建议:

12.2.1 模型选择

AgentScope Java 支持多种模型提供商,选择时需权衡能力、成本、响应速度。

模型系列 特点 适用场景 成本
通义千问(DashScope) 国内访问快,功能全面,支持 VL 大多数企业应用,对数据隐私要求中等 按量计费,中等
OpenAI 能力最强,但国内访问可能受限 需要最前沿能力的场景,有海外业务 较高
Ollama(本地) 数据完全私有,无需网络,低延迟 数据敏感行业(金融、医疗),离线环境 硬件成本

建议

  • 国内通用场景优先选择 通义千问,集成最方便。
  • 需要视觉能力时,使用 qwen-vl-plusqwen-vl-max
  • 本地私有化部署可考虑 Ollama + llama3 等,但需注意模型能力可能稍弱。

12.2.2 工具沙箱配置

安全是生产环境的重中之重。务必为工具配置严格的沙箱规则:

  • 最小权限原则 :默认禁止所有(default-policy: deny),然后按需开放。
  • 文件系统 :只允许工具访问特定目录(如 /data/app/uploads),且设为只读。
  • 网络:只允许访问必要的域名(如天气 API、公司内网服务),禁止内网 IP 探测。
  • 进程:除非绝对必要,否则禁止执行系统命令。如需执行,应在单独的容器中运行。

12.2.3 记忆持久化

  • 短期记忆 :使用 TokenWindowMemory 控制上下文长度,避免超出模型限制。
  • 长期记忆 :使用 Redis 或数据库存储用户偏好、关键信息。可参考第十一章的 UserMemoryService
  • 生产环境:建议为每个用户/会话创建独立的记忆实例,并用 Redis 的过期时间自动清理不活跃会话。

12.2.4 性能与成本优化

  • 缓存:对重复性问题(如常见 FAQ),在智能体外层加一层缓存(如 Redis),直接返回缓存结果,避免调用模型。
  • 并发控制 :在 Runtime 中为每个智能体设置 max-concurrent,防止突发流量压垮后端模型 API。
  • Token 监控:通过 Studio 或自定义指标记录 Token 消耗,设置告警阈值。
  • 模型降级 :对简单问题使用 qwen-turbo,复杂问题使用 qwen-max。可在智能体内根据问题长度或关键词动态选择模型。

12.2.5 可观测性

  • 强制在生产环境开启 Studio 的指标收集,但注意不要将 Studio UI 暴露在公网。
  • 关键指标:请求量、平均耗时、错误率、Token 用量。
  • 使用 Prometheus + Grafana 构建监控大盘,或对接阿里云 ARMS。

12.2.6 多智能体 vs 工作流

  • 对于流程确定、需要审计的业务(如订单处理),优先使用 Pipeline 工作流
  • 对于开放、需要动态决策的任务(如个人助手),优先使用 多智能体协作 + ReAct
  • 二者可以结合:外层用 Pipeline 控制流程,内层用 ReAct 智能体处理复杂子任务。

12.3 后续学习路径

你已经掌握了 AgentScope Java 的核心,下一步可以朝以下方向深入:

  1. 关注官方动态 :AgentScope 正在快速发展,关注 GitHub 仓库官方文档,及时了解新特性。
  2. 深入 ReAct 与提示词工程:ReAct 的效果高度依赖提示词。学习如何设计高质量的 System Prompt,让智能体更好地遵循指令。
  3. 探索 AgentScope Python 版:Python 版功能更丰富,可以作为借鉴,了解未来 Java 版可能的发展方向。
  4. 实践项目:选择一个真实业务场景(如智能客服、代码审查助手、会议纪要助理),用 AgentScope Java 完整实现,并部署上线。
  5. 参与社区:给项目点 Star,提交 Issue 或 PR,与其他开发者交流。

12.4 写在最后

通过本教程,你不仅学会了 AgentScope Java 的技术细节,更重要的是建立了一套构建智能体应用的思维框架。从零开始,你已经能够:

  • 让智能体拥有记忆,记住用户说过的话。
  • 让智能体调用工具,获取实时信息、执行操作。
  • 让多个智能体协作,像团队一样完成复杂任务。
  • 让智能体进入 ReAct 循环,自主思考与纠错。
  • 用工作流编排确定性流程,满足企业级要求。
  • 将智能体安全部署为服务,并用 Studio 监控它。
  • 让智能体看懂图片、画出图片,并记住用户的长期偏好。

AI 技术日新月异,但核心的思维模式------将智能体视为自主的、协作的、有记忆的实体------将伴随你不断前行。


附录

A. 常见问题解答(FAQ)

Q1: 为什么我的智能体不调用工具? A: 检查以下几点:

  • 工具类是否有 @Tool 注解,且是 Spring Bean(@Component)。
  • 工具描述是否清晰,能让模型理解何时调用。
  • 如果你用的是 BaseAgent,它默认不会自动调用工具。需要使用 ReActAgent 或继承 BaseAgent 并手动处理工具调用逻辑。

Q2: 如何调试 ReAct 智能体的思考过程? A: 启用 Studio(第十章),通过 UI 查看执行轨迹。或者在代码中增加日志,打印智能体每次的输入输出。

Q3: 沙箱报错"权限不足"怎么办? A: 检查沙箱配置(application.yml)中的 allowed-pathsallowed-hosts 是否包含了工具需要访问的资源。按需放宽权限,但务必遵循最小权限原则。

Q4: 多智能体系统中,消息收不到? A: 确认所有智能体都注入了同一个 MessageHub Bean(默认是单例)。检查发送时指定的接收者名称是否与接收智能体的 getName() 一致。接收方需要定期调用 receive() 或开启定时任务处理消息。

Q5: Studio 无法访问? A: 检查依赖是否完整,application.ymlagentscope.studio.enabled=true。确认端口没有被占用,访问路径是否正确(默认 /studio)。查看启动日志,看 Studio 是否成功初始化。

Q6: 通义千问 VL 模型调用返回"模型不支持"怎么办? A: 确认你开通了 VL 模型的权限。在 DashScope 控制台中,需要单独开通 qwen-vl-plusqwen-vl-max 的体验(通常免费)。检查 model 名称是否拼写正确。

Q7: 如何估算 Token 消耗? A: Studio 的评估报告中会显示 Token 用量。也可以自己在智能体中记录 ChatResponse 的元数据。通义千问的响应中通常包含 usage 字段。

B. 完整代码示例仓库地址

为了方便你查阅和运行,本教程的所有代码示例已整理到一个 Git 仓库中:

👉 gitee.com/youhei/agen...

仓库按章节组织,每个示例包含完整的 Java 类和配置文件,并附有 README 说明。

C. 参考资源链接

相关推荐
我爱吃土豆11192 小时前
从零到上架:Chrome 新标签页生产力扩展 FocusTab
前端·产品
敲代码的约德尔人2 小时前
我在 3 个项目中踩坑后,才真正理解了 JavaScript 设计模式
前端·javascript
子淼8122 小时前
Kali Linux 入门指南:基础操作与常用指令解析
前端
QYR市场调研2 小时前
低密度聚乙烯市场竞争格局变化趋势
前端
Java水解2 小时前
Spring Boot 数据缓存与性能优化
spring boot·后端
我爱娃哈哈2 小时前
SpringBoot + 网关流量染色 + 测试环境隔离:线上流量复制到预发环境,零风险验证
后端
@atweiwei2 小时前
Tokio 深度解析:Rust 异步运行时与 Go 协程对比指南
服务器·网络·后端·golang·rust·内存·所有权
学以智用2 小时前
Vue 3 组件完全指南
前端·vue.js