Solon v4.0.6

chat - 模型的结构化输出

</> markdown
2026年8月20日 下午11:44:50

结构化输出(Structured Output),是指让大模型按给定的数据结构(JSON Schema)返回结果,再反序列化为 Java 对象。适合信息提取、数据入库、结果校验等场景。

Solon AI 的结构化输出有三个特点:

  • 类型即契约:直接用一个 Java Bean 声明输出结构,框架自动生成 JSON Schema
  • 接口标准无关:Schema 以指令方式注入提示语,不依赖模型的原生 json mode,任何模型都能用
  • 容错提取:响应消息自带容错的 JSON 提取与 toBean 一步转换

1、定义输出的 Java Bean

用普通的 Java 类 + @Param 注解即可(@Param 的描述、必填、默认值会并入生成的 Schema 字段):

public class ResumeInfo {
    @Param(description = "姓名", required = true)
    private String name;

    @Param(description = "年龄")
    private Integer age;

    @Param(description = "邮箱")
    private String email;

    @Param(description = "技能列表", defaultValue = "[]")
    private List<String> capabilities;

    // get set ...
}

2、三层 API:在哪里声明 outputSchema

结构化输出的 schema 可以在三个层面声明,作用范围从大到小:

  • 模型级:构建 ChatModel 时指定,对该模型的所有请求生效
ChatModel chatModel = ChatModel.of("https://api.openai.com/v1/chat/completions")
        .apiKey(apiKey)
        .model("gpt-4o")
        .outputSchema(ResumeInfo.class) //模型级
        .build();
  • 请求级:在某次请求的选项里指定,只影响本次请求(两种写法均可,Consumer 形式更简洁)
AssistantMessage message = chatModel
        .prompt("请从下面的简历文本中提取关键信息:..." + resumeText)
        .options(o -> o.outputSchema(ResumeInfo.class)) //请求级
        .call()
        .getMessage();
  • 智能体级:构建 Agent 时指定(需引入 solon-ai-agent 依赖)
ReActAgent resumeAgent = ReActAgent.of(chatModel)
        .name("ResumeExtractor")
        .instruction("请从用户提供的文本中提取关键信息")
        .outputSchema(ResumeInfo.class) //智能体级
        .outputKey("extracted_resume")  //结果同时写入会话上下文
        .build();

如果只有 schema 字符串(比如来自外部配置),也可以直接用 outputSchema(String) 重载。

3、结果提取与反序列化

响应消息 AssistantMessage 提供了三个层次的取值能力:

AssistantMessage message = chatModel.prompt("...").call().getMessage();

//a) 原始内容(含思维链时也会包含)
String rawContent = message.getContent();

//b) 容错提取的 JSON 片段(自动剥离 markdown 围栏与前后缀文本)
String json = message.getJsonContent();

//c) 一步转为 Java Bean
ResumeInfo info = message.toBean(ResumeInfo.class);

其中 getJsonContent() 会定位首个 {(或 [)与末个 }(或 ])之间的内容,模型偶尔加上 ```json 围栏或说明文字也不会失败。

4、传输机制:为什么不依赖模型的 json mode

Solon AI 在组装请求时,如果发现选项里有 outputSchema,会把它包装成指令片段追加到提示语里:

<output_schema>
{...生成的 JSON Schema...}
</output_schema>

这个动作由 ChatDialect.prepareOutputSchemaInstruction(...) 完成,属于接口标准(方言)层的能力。因此:

  • 不需要模型支持原生的 response_format: json_schema,Ollama、OpenAI、DashScope、Gemini、Anthropic 等都能用
  • 有特殊要求的模型,可通过定制方言覆写该行为

另外,String、数字、布尔、枚举、日期等简单类型会被自动忽略 schema 生成(它们不需要结构约束)。

5、智能体的结构化输出与下游联动

智能体场景下,outputKey 会把提取结果写入会话上下文,供工作流的下游节点消费:

AgentSession session = InMemoryAgentSession.of("demo");

AssistantMessage message = resumeAgent
        .prompt(Prompt.of(resumeText))
        .session(session)
        .call()
        .getMessage();

//从会话上下文取结构化结果
String extracted = (String) session.getContext().get("extracted_resume");

6、生产化建议

  • temperature(0.1) 抑制格式漂移;解析失败可配合重试使用(Agent 有 retryConfig(maxRetries, retryDelayMs)
  • schema 只能约束"形状",管不住"算术"——求和、统计之类的计算应放到 Java 里做
  • 需要强校验的入库场景,建议在 toBean 之后再走一遍业务校验