【AgentScope Java新手村系列】(4)结构化输出

简介: 结构化输出 — JSON Schema 约束 LLM 输出格式,直接反序列化为 Java POJO,打通文本到对象的转换。

第四章 结构化输出:用 JSON Schema 让 Agent 直接返回 Java POJO

"让 LLM 返回自然语言容易,让它返回强类型的 Java 对象很难——除非用 JSON Schema 约束。本章演示这个'文本到对象'转换的关键技巧。"

4.1 为什么需要结构化输出

默认情况下,Agent 返回的是自然语言文本。但在很多场景下,我们需要 Agent 返回结构化的数据:

  • 从文本中提取信息(姓名、邮箱、电话)
  • 分类任务(情感分类、意图识别)
  • 数据生成(产品描述、测试数据)

AgentScope Java 支持让 Agent 返回指定 Java 类型的数据。2.0 沿用 1.x 的 @StructuredOutput + JSON Schema 机制,并在 Msg 上提供 getStructuredData(Class) 读取入口。

4.2 基本用法

定义输出类型

public static class ProductRequirements {
   
    public String productType;
    public String brand;
    public Integer minRam;
    public Double maxBudget;
    public List<String> features;

    public ProductRequirements() {
   }  // 必须有无参构造函数
}

注意事项:

  • 类必须有无参构造函数
  • 字段使用 public 修饰(或提供 getter/setter)
  • 支持基本类型、String、List、嵌套对象
  • 2.0 推荐在字段上加 com.fasterxml.jackson.annotation.JsonPropertyDescription,让生成的 JSON Schema 描述更清晰——LLM 填充准确率更高

调用时指定类型

import io.agentscope.core.message.UserMessage;
import io.agentscope.core.agent.RuntimeContext;

UserMessage userMsg = new UserMessage(
        "我需要一台16GB内存、苹果品牌、预算2000美元左右的笔记本电脑");

// 传入 Class 对象
Msg msg = agent.call(userMsg, ProductRequirements.class, RuntimeContext.empty()).block();

// 获取结构化数据
ProductRequirements result = msg.getStructuredData(ProductRequirements.class);
System.out.println("Product: " + result.productType);
System.out.println("Brand: " + result.brand);
System.out.println("RAM: " + result.minRam + " GB");
System.out.println("Budget: $" + result.maxBudget);

2.0 的 call(messages, returnType, ctx) 形式与 1.x 的 call(messages, returnType) 形式并存;新代码请统一传 RuntimeContext

4.3 完整示例

以下方案通过提示词要求返回 JSON + 手动反序列化,不依赖 response_format 参数,所有模型(含 DeepSeek)都支持。

package com.example;

import com.fasterxml.jackson.databind.ObjectMapper;
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 java.util.List;

public class StructuredOutputExample {
   

    private static final ObjectMapper MAPPER = new ObjectMapper();

    /** 从 LLM 回复中提取第一个 JSON 对象,忽略前后的自然语言 */
    private static String extractJson(String raw) {
   
        int start = raw.indexOf('{');
        int end = raw.lastIndexOf('}');
        if (start != -1 && end > start) {
   
            return raw.substring(start, end + 1);
        }
        throw new IllegalArgumentException("No JSON found: " + raw);
    }

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

        ReActAgent agent = ReActAgent.builder()
                .name("AnalysisAgent")
                .sysPrompt("你是一个智能分析助手,始终输出纯 JSON,不要包含其他文字。")
                .model(OpenAIChatModel.builder()
                        .apiKey(apiKey)
                        .modelName("deepseek-chat")
                        .baseUrl("https://api.deepseek.com")
                        .stream(true)
                        .formatter(new OpenAIChatFormatter())
                        .build())
                .toolkit(new Toolkit())
                .build();

        RuntimeContext ctx = RuntimeContext.empty();

        // 示例 1:提取产品信息
        System.out.println("=== Product Requirements ===");
        String reply1 = agent.call(
                new UserMessage("提取产品需求:我需要一台16GB内存、苹果品牌、"
                        + "预算2000美元左右的笔记本电脑。"
                        + "请输出 JSON:{\"productType\":\"类型\", \"brand\":\"品牌\","
                        + " \"minRam\":16, \"maxBudget\":2000, \"features\":[\"特性\"]}"),
                ctx
        ).block().getTextContent();

        ProductRequirements product = MAPPER.readValue(extractJson(reply1), ProductRequirements.class);
        System.out.println("Product Type: " + product.productType);
        System.out.println("Brand: " + product.brand);
        System.out.println("Min RAM: " + product.minRam + " GB");

        // 示例 2:情感分析
        System.out.println("\n=== Sentiment Analysis ===");
        String reply2 = agent.call(
                new UserMessage("分析情感:这个产品超出了我的预期!质量很棒但配送速度慢。"
                        + "请输出 JSON:{\"sentiment\":\"正面\", \"score\":0.95, \"summary\":\"总结\"}"),
                ctx
        ).block().getTextContent();

        SentimentAnalysis sentiment = MAPPER.readValue(extractJson(reply2), SentimentAnalysis.class);
        System.out.println("Overall: " + sentiment.sentiment);
        System.out.println("Score: " + sentiment.score);
    }

    public static class ProductRequirements {
   
        public String productType;
        public String brand;
        public Integer minRam;
        public Double maxBudget;
        public List<String> features;
        public ProductRequirements() {
   }
    }

    public static class SentimentAnalysis {
   
        public String sentiment;
        public Double score;
        public String summary;
        public SentimentAnalysis() {
   }
    }
}

4.4 流式结构化输出

也可以通过 streamEvents() 拿到结构化输出(推荐):

import io.agentscope.core.event.AgentEvent;
import io.agentscope.core.event.AgentEventType;
import io.agentscope.core.event.AgentEndEvent;
import io.agentscope.core.message.UserMessage;
import io.agentscope.core.agent.RuntimeContext;
import reactor.core.publisher.Flux;

Flux<AgentEvent> eventFlux = agent.streamEvents(
        new UserMessage("..."),
        ProductRequirements.class,
        RuntimeContext.empty());

AgentEndEvent end = eventFlux.filter(e -> e.getType() == AgentEventType.AGENT_END)
        .blockLast()
        .map(e -> (AgentEndEvent) e)
        .orElseThrow();

ProductRequirements result = end.getMessage().getStructuredData(ProductRequirements.class);

1.x 风格的 agent.stream(msg, opts, type) 仍可工作但已标注 @Deprecated(forRemoval = true),新代码请用 streamEvents(...)

4.5 支持的字段类型

Java 类型 JSON Schema 类型
String string
Integer, int integer
Double, double, Float, float number
Boolean, boolean boolean
List<T> array
Map<String, Object> object
嵌套对象 object
Java record object(2.0 起官方推荐用 record,更简洁)

4.5.1 用 record 简化定义

2.0 推荐用 Java 17 的 record 来表达输出类型——无样板代码、字段自带 getter:

public record ProductRequirements(
        @JsonPropertyDescription("产品类型,例如 laptop / phone / tablet")
        String productType,
        @JsonPropertyDescription("品牌,例如 Apple / Dell / Lenovo")
        String brand,
        @JsonPropertyDescription("最小内存,单位 GB")
        Integer minRam,
        @JsonPropertyDescription("最高预算,单位美元")
        Double maxBudget,
        @JsonPropertyDescription("用户提到的特性关键词列表")
        List<String> features
) {
   }

加上 @JsonPropertyDescription 之后,生成的 JSON Schema 描述会带上字段说明——LLM 填充时知道每个字段的"业务含义",准确率显著提升。

4.5.2 嵌套对象示例

public static class Address {
   
    public String street;
    public String city;
    public String country;
    public Address() {
   }
}

public static class Person {
   
    public String name;
    public Integer age;
    public Address address;        // 嵌套对象
    public List<String> hobbies;   // 列表
    public Person() {
   }
}

4.6 工作原理

当你传入一个 Class 对象时,框架会:

  1. 使用 jsonschema-generator 根据 Java 类生成 JSON Schema(@JsonPropertyDescription / @JsonProperty 都会反映到 schema 上)
  2. 将 JSON Schema 作为约束发送给 LLM(通过 response_format 参数或 system prompt 注入)
  3. LLM 按照 Schema 格式输出 JSON
  4. 框架将 JSON 反序列化为 Java 对象
  5. 将对象放入 Msg 的结构化数据字段(msg.getStructuredData(Class) 读取)

这个过程对用户是透明的,你只需要定义 Java 类即可。

4.6.1 模型兼容性说明

结构化输出有两种实现方式:

方式 原理 模型兼容性
API 参数约束agent.call(msg, SomeClass.class, rt) 框架向 API 发送 response_format 参数,强制服务器校验输出为 JSON 仅 OpenAI 等部分模型支持
提示词驱动(本章示例的做法) 在 UserMessage 中写"请输出 JSON 格式:{...}",让 LLM 按格式输出 所有模型都支持

本章的完整示例采用提示词驱动方式,因此不挑模型——DeepSeek、通义千问等均可正常使用。

如果误用了方式一(agent.call(msg, SomeClass.class, rt)),不支持的模型会报:

"This response_format type is unavailable now"

此时改用本章示例的方式即可。

4.7 最佳实践

  1. 字段名要有意义:LLM 会根据字段名理解应该填什么内容
  2. 使用合适的类型:数字用 Integer/Double,不要都用 String
  3. List 用于多值字段:当一个字段可能有多个值时,使用 List
  4. @JsonPropertyDescription:为每个字段写一句话业务描述,准确率提升明显
  5. 系统提示词配合:在 sysPrompt 中说明输出要求,提高准确率
  6. 处理异常:LLM 输出可能不符合预期,需要 try-catch 处理
try {
   
    Msg msg = agent.call(userMsg, ProductRequirements.class, ctx).block();
    ProductRequirements result = msg.getStructuredData(ProductRequirements.class);
    // 使用 result
} catch (Exception e) {
   
    System.err.println("Failed to parse structured output: " + e.getMessage());
}

4.8 2.0 增量:结构化输出与子 agent 协作

如果你在 HarnessAgent 里用子 agent 处理"先调研再汇总"的场景,可以让子 agent 返回结构化结果,主 agent 自动拿到强类型:

// 主 agent 调用子 agent,子 agent 内部 call(..., Report.class, ctx) 返回 Report
// 主 agent 拿到的 tool_result 是 Report 的 JSON 序列化
// 主 agent 的下一轮推理基于这份结构化结果继续

workspace/subagents/researcher.md 里可以显式说明子 agent 的输出 schema(用自然语言),主 agent 就能稳定地消费。

目录
相关文章
|
NoSQL Java 关系型数据库
【AgentScope Java新手村系列】(5)记忆与会话管理
记忆与会话管理 — AgentState 管理上下文窗口,AgentStateStore 持久化,RuntimeContext.sessionId 隔离多用户会话。
131 0
|
3天前
|
自然语言处理 Java API
【AgentScope Java新手村系列】(7)子Agent编排
子Agent编排 — SubagentDeclaration 描述子 agent,主 agent 通过 agent_spawn 工具同步/异步委派子任务。
108 0
|
6天前
|
人工智能 前端开发 API
通义灵码新品深度体验:当编程智能体遇上 MCP,3000+ 工具让 AI 编码进入新时代
通义灵码全新版本重磅发布,深度适配 Qwen3 大模型,正式上线编程智能体能力,并率先集成魔搭 MCP 广场 3000+ 工具。本文从智能体自主编程、MCP 工具集成、记忆感知、工程感知四个维度进行深度体验,通过三个真实编程场景验证新一代 AI 编码助手的实际效果,并在最后给出选型建议和最佳实践。
163 2
|
5天前
|
人工智能 前端开发 数据挖掘
全链路实战:依托Codex完成PPT、数据分析、网页与APP一站式AI开发教程
在AI技术飞速迭代的当下,代码生成早已不是AI工具的单一能力边界。OpenAI旗下的Codex经过持续升级,如今已经成长为一款综合性智能生产力平台,除了经典的代码编写能力外,还支持插件调用、电脑远程操控、数据分析、多媒体制作、全品类应用开发等多元功能。本文将结合完整实操流程,一步步演示如何使用Codex完成PPT制作、体育赛事数据分析预测、网页开发以及移动端APP开发四大核心场景,全程记录操作指令、执行过程、代码实现以及问题优化方案,直观展现AI如何重塑传统工作与开发流程,同时剖析这套全链路AI工作模式的优势与现存局限。整套流程无需深厚的专业功底,普通办公人员、初级开发者都可以参考落地。
189 1
|
6天前
|
人工智能 安全 决策智能
欢迎报名丨2026 Agentic AICon—智能体基础设施与 AgentOps 专场,邀您参会
6 月 5 日上海,2026 Agentic AICon「智能体基础设施与 AgentOps」专场,聚焦 Agent 规模化落地的基础设施层,覆盖从构建、部署到规模化运行的全生命周期,为企业智能体工程化落地提供完整路径。
|
6天前
|
人工智能 安全 前端开发
10|Agent Harness 的未来:从代码助手到工程协作系统
AI编程正迈入第三阶段——Agent Harness:AI不再仅补全代码或回答问题,而是深度融入研发全流程——读仓库、改文件、跑测试、连工具、协作者。未来核心在于“可治理的工程协作”,而非单纯自动化。(239字)
99 8
|
6天前
|
人工智能 安全 前端开发
面试官问:什么是 Harness 工程?AI Agent 时代,测试人必须补上的新能力
Harness工程是AI Agent时代的“工作台”,聚焦为其构建稳定、可控、可验证的工程环境。它涵盖上下文管理、工具调用、沙箱权限、测试验证、日志观测与反馈回路,解决Agent在真实项目中因缺上下文、缺工具、缺反馈、缺边界导致的失控问题。本质是让Agent“能做事、做得对、出错可修复”。
|
5天前
|
人工智能 缓存 监控
构建企业级 AI Agent 工程化实践:从原型到生产环境的跨越
本文深入探讨企业级AI Agent从原型到生产的工程化实践,直面LLM概率性与业务确定性的根本矛盾,提出“LLM负责感知推理、代码保障逻辑执行”的混合架构。系统阐述可观测性、安全护栏、性能优化、数据管理四大工程支柱,并结合IT运维、金融合规等实战场景,提供可落地的LLMOps方法论。
|
2月前
|
机器学习/深度学习 缓存 测试技术
DeepSeek-V4开源:百万上下文,Agent能力比肩顶级闭源模型
DeepSeek-V4正式开源!含V4-Pro(1.6T参数)与V4-Flash(284B参数)双版本,均支持百万token上下文。首创混合注意力架构,Agent能力、世界知识与推理性能全面领先开源模型,数学/代码评测比肩顶级闭源模型。
4394 10
|
人工智能 JavaScript Java
【SpringAIAlibaba新手村系列】(1)初识 Spring AI Alibaba 框架
本文介绍了SpringAIAlibaba框架的基本概念和使用方法。作为Spring官方AI框架的阿里云实现版本,它简化了Java开发者调用AI模型的过程。文章详细讲解了核心概念如ChatModel、ChatClient,以及阿里云百炼平台的功能。通过HelloWorld项目示例,展示了如何配置APIKey、编写控制层代码,实现普通调用和流式输出两种AI交互方式。重点阐述了SpringAI与SpringAIAlibaba的关系,以及自动配置机制的工作原理,帮助开发者快速上手这一框架。
5973 5

热门文章

最新文章