LangChain4j-OpenAI - 结构化输出及优化

为什么要使用"结构化输出"?

核心是让AI回复json格式的结果 ,便于我们按照实际业务需求进行解析

虽然已经能够调用AI生成代码,但直接返回字符串的方式不便于后续解析 代码并保存为文件。因此我们需要将AI的输出转换为结构化的对象,利用LangChain4j的结构化输出特性可以轻松实现。

一、结构化输出【基本实现】

1. 创建AI回复的json格式结果(即model数据模型)

java 复制代码
@Data
public class HtmlCodeResult {

    private String htmlCode;

    private String description;
}

2. 修改AI服务接口,让方法返回结构化对象

java 复制代码
/**
 * 生成 HTML 代码
 *
 * @param userMessage 用户消息
 * @return 生成的代码结果
 */
@SystemMessage(fromResource = "prompt/codegen-html-system-prompt.txt")// 系统提示词
HtmlCodeResult generateHtmlCode(String userMessage);

3. 使用AiServices.create创建AI服务接口generateHtmlCode的proxy代理对象(同时传入ChatModel与模型交互客户端,包含base_url、api_key、mode-name等信息)

本质是利用工厂模式使用create方法创建统一的代理对象。

java 复制代码
@Configuration
public class AiCodeGeneratorServiceFactory {

    @Resource
    private ChatModel chatModel;

    @Bean
    public AiCodeGeneratorService aiCodeGeneratorService() {
        return AiServices.create(AiCodeGeneratorService.class, chatModel);
    }
}

4. 编写单元测试方法进行验证

java 复制代码
@SpringBootTest
class AiCodeGeneratorServiceTest {

    @Resource
    private AiCodeGeneratorService aiCodeGeneratorService;

    @Test
    void generateHtmlCode() {
        HtmlCodeResult result = aiCodeGeneratorService.generateHtmlCode("做个程序员algo的计算器小工具,只实现加法计算逻辑,不要写复杂代码,禁止扩展功能");
        Assertions.assertNotNull(result);
    }
}

可以看到,输入时userMessage向模式传递了结构化输出的json格式:

而输出时,结果也遵循我们的json输出格式:

如此,我们可以实现基本的结构化输出需求。

二、OutputStruct的优化技巧(重点json_schema,其它了解即可)

参考文档:【结构化输出 - 千问AI平台】【JSON Output | DeepSeek API Docs

1. 设置max_tokens(看需求进行设置,一般禁用即可)

设置原则:

若模型输出发现缺少内容,被截断部分,可以调大max_tokens;

若模型当前情况输出没有异常,禁用max_tokens,防止消耗过多token造成成本增加。

设置一下输出长度,防止AI生成的json被半路截断,AI返回给我们的内容显示不全。

如qwen3.8-flash:【Qwen3.8-Flash - 千问AI平台

最大输出是131k,那我们的max_tokens最大值为131 * 1024 = 134144

bash 复制代码
langchain4j:
  open-ai:
    chat-model:
      max-tokens: 134144 # 1024 * 131

2. 使用json_object使模型返回json格式响应内容

参考文档:【JSON Output | DeepSeek API Docs

yml设置:(基于前面的基本实现,我们只需要添加yml中关于json_object的配置即可)

bash 复制代码
langchain4j:
  open-ai:
    chat-model:
      strict-json-schema: true
      response-format: json_object

用户传入的 system 或 user prompt 中必须含有 json 字样 ,并给出希望模型输出的 JSON 格式的样例,以指导模型来输出合法 JSON:

效果:

向模型输入:

模型输出:

3. 使用json_schema使模型严格返回json格式⭐(必须掌握)

参考文档 【OpenAI | LangChain4j】【结构化输出 - 千问AI平台

JSON Schema 模式下,提示词无需包含 "JSON" 关键词。
要求: system_prompt中一定要包含json字样的提示。

缺点:AI回复的json格式可能不统一,若需要传递给下游服务器,可以重试或者使AI生成新的json格式。但建议生产环境使用严格的json_schema格式传递给下游。

LangChain4j-OpenAI中设置json_schema:(OpenAIChatModel的builder对象属性对应着我们yml中关于chatModel的配置属性)

使用步骤如下:

1. 设置yml:(开启严格模式的json_schema)

bash 复制代码
langchain4j:
  open-ai:
    chat-model:
      strict-json-schema: true # 强制严格遵循 schema 定义
      response-format: json_schema # 开启 json_schema 模式

2. 定义输出POJO(基本实现中已定义)

java 复制代码
@Data
public class HtmlCodeResult {

    private String htmlCode;

    private String description;
}

3. 定义AI服务接口,【模型输出结果格式】指定json_schema (已定义)

java 复制代码
/**
 * 生成 HTML 代码
 *
 * @param userMessage 用户消息
 * @return 生成的代码结果
 */
@SystemMessage(fromResource = "prompt/codegen-html-system-prompt.txt")// 系统提示词
HtmlCodeResult generateHtmlCode(String userMessage);

效果如下:

向模式输入(可以看到json_schema形式标准字段):

模型输出:(包含required必须字段)

4. 添加POJO的字段描述⭐(为结果类和属性添加详细的描述信息,便于Al理解)

参考文档【Structured Outputs | LangChain4j

POJO添加@Description:

java 复制代码
/**
 * html代码的结构化输出json-schema
 */
@Description("生成 HTML 代码文件的结果\"")
@Data
public class HtmlCodeResult {
    @Description("HTML代码")
    private String htmlCode;

    @Description("生成代码的描述")
    private String description;
}

效果如下:

输入给模型:

模型输出:

5. 提示词优化

参考文档:【结构化输出 - 千问AI平台

系统提示词明确要求输出JSON格式,这样可以进一步提高成功率

要求:

  1. system_prompt有json字眼,不区分大小写

  2. 给出可供模型参考的json返回格式样例,供模型学习(参考示例可以给出多场景你期望的json返回格式)

system_prompt演示:

bash 复制代码
请从用户输入中提取个人信息并按照指定的JSON Schema格式输出:

【输出格式要求】
输出必须严格遵循以下JSON结构:
{
  "info": {
    "name": "字符串类型,必需字段,用户姓名",
    "age": "字符串类型,必需字段,格式为'数字+岁',例如'25岁'",
    "email": "字符串类型,必需字段,标准邮箱格式,例如'user@example.com'"
  },
  "hobby": ["字符串数组类型,非必需字段,包含用户的所有爱好,如未提及则完全不输出此字段"]
}

【字段提取规则】
1. name: 从文本中识别用户姓名,必需提取
2. age: 识别年龄信息,转换为"数字+岁"格式,必需提取
3. email: 识别邮箱地址,保持原始格式,必需提取
4. hobby: 识别用户爱好,以字符串数组形式输出,如未提及爱好信息则完全省略hobby字段

【参考示例】
示例1(包含爱好):
Q:我叫张三,今年25岁,邮箱是zhangsan@example.com,爱好是唱歌
A:{"info":{"name":"张三","age":"25岁","email":"zhangsan@example.com"},"hobby":["唱歌"]}
示例2(包含多个爱好):
Q:我叫李四,今年30岁,邮箱是lisi@example.com,平时喜欢跳舞和游泳
A:{"info":{"name":"李四","age":"30岁","email":"lisi@example.com"},"hobby":["跳舞","游泳"]}
示例3(不包含爱好):
Q:我叫赵六,今年28岁,我的邮箱是zhaoliu@example.com
A:{"info":{"name":"赵六","age":"28岁","email":"zhaoliu@example.com"}}
示例4(不包含爱好):
Q:我是孙七,35岁,邮箱sunqi@example.com
A:{"info":{"name":"孙七","age":"35岁","email":"sunqi@example.com"}}

请严格按照上述格式和规则提取信息并输出JSON。如果用户未提及爱好,则不要在输出中包含hobby字段。

6. 生产环境使用建议

参考文档【结构化输出 - 千问AI平台

建议:

  1. 禁用max_tokens

  2. 使用json_schema

  3. 使用 SDK 辅助生成 Schema,避免手动维护出错

相关推荐
zhojiew1 小时前
让 Agent 安全地“借用别人的钥匙“:AgentCore 上用 Authentik 打通 OAuth 出站的一次实战
安全·ai·aws·agentcore
苏灿烤鱼2 小时前
当程序员遇到装修:用 AI 画 CAD、搭房子,甚至自己做家具
开源·agent·mcp
天天喝旺仔2 小时前
AI 编程实战:用 Cursor + Claude Code + Copilot 把效率翻倍
ide·chatgpt·prompt·copilot·ai编程
秋饼2 小时前
Spring AI 2.0 多模型路由网关:Astra/Sol 到国产模型的智能调度与降级
java·ai·技术分享·后端开发
Sincerelyplz2 小时前
【Pipecat】一个能对话、会用工具的语音 Agent,是怎么工作的?
开源·agent
DolphinDB2 小时前
Text-to-SQL 已过时?DolphinX 正在重新定义 AI 问数
ai·时序数据库·dolphindb
lindd9119113 小时前
github项目Readme文档制作与远程推送(全自动化上传)
ai·github·ai的skill使用
Web极客码3 小时前
GPT-6 Astra 上手体验:如何让编码代理真正提速
服务器·gpt·ai·大模型·astra
GHL2842710904 小时前
豆包换脸学习
学习·ai