行业资讯 2025年08月6日
0 收藏 0 点赞 299 浏览 5233 个字
摘要 :

文章目录 一、响应模型的定义 二、提取器接口与AiServices的运用 三、处理响应解析错误 四、完整示例代码 五、总结 在开发与大语言模型(LLM)交互的应用时,获取结构……




  • 一、响应模型的定义
  • 二、提取器接口与AiServices的运用
  • 三、处理响应解析错误
  • 四、完整示例代码
  • 五、总结

在开发与大语言模型(LLM)交互的应用时,获取结构化输出至关重要。今天咱们就深入探讨LangChain4j如何实现结构化JSON输出,通过实际的代码示例,让大家掌握如何从LLM获取特定格式的响应,并将其填充到模型对象中。这篇文章会涵盖响应模型的定义、接口和服务的使用、错误处理等关键内容,非常适合想要深入了解LangChain4j在结构化输出方面应用的朋友。

一、响应模型的定义

假设在我们的应用程序里有一个Person记录,这个记录就像是一个用来装个人信息的“小盒子”,它包含了name(姓名)、age(年龄)、city(城市)和country(国家)这四个字段。我们希望从LLM模型的响应中提取这些信息,为每一个响应创建一个新的Person对象。定义Person记录的代码如下:

record Person(String name, int age, String country, String city) {}

这个record关键字就像是一个快捷方式,用它定义的Person记录,简洁地描述了我们需要的个人信息结构。

二、提取器接口与AiServices的运用

在LangChain4j里,有个很实用的功能,和HttpExchange或者Retrofit有点像,我们可以创建带有期望API的声明式接口,然后让LangChain4j帮我们生成一个实现这个接口的对象(代理对象)。这个代理对象可厉害啦,它能把和LLM交互时那些复杂的操作都“藏起来”,还能帮我们完成一些常用的重复性任务,比如给LLM格式化输入内容,还有解析LLM返回的输出结果。

下面来看个例子,我们创建一个PersonExtractor接口,这个接口里有个方法,专门用来从一段没有特定格式的文本里,提取出结构化的JSON格式的个人信息。这里的@UserMessage注解就像是给LLM的一个“小纸条”,上面写着提取信息的具体要求,比如让它只用JSON格式返回信息,而且不要带任何Markdown格式的标记。代码如下:

interface PersonExtractor {
    @UserMessage(\"\"\"
        Extract the name, age. city and country of the person described below.
        Return only JSON, without any markdown markup surrounding it.
        Here is the document describing the person:
        ---
        {{it}}
        \"\"\")
    Person extract(String text);
}

接下来,我们可以用AiServices.create()方法创建这个接口的代理对象,这个方法利用了Java的动态代理机制。在创建代理对象之前,我们得先创建一个聊天语言模型实例,这里以OpenAI的gpt-3.5-turbo模型为例。代码如下:

// 创建聊天语言模型实例,使用OpenAI的gpt-3.5-turbo模型,并配置API密钥、开启请求和响应日志记录
ChatLanguageModel model = OpenAiChatModel.builder()
      .apiKey(OPENAI_API_KEY)
      .modelName(OpenAiChatModelName.GPT_3_5_TURBO)
      .logRequests(true)
      .logResponses(true)
      .build(); 
// 创建PersonExtractor接口的代理对象,将接口和模型实例传入
PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, model); 

现在,我们就可以用personExtractor这个对象来调用extract()方法啦。把一段描述人的文本作为参数传进去,它就能返回一个Person对象。比如:

// 定义一段描述人的文本
String inputText = \"\"\"
    Charles Brown, aged 56, resides in the United Kingdom. Originally 
    from a small town in Devon Charles developed a passion for history 
    and archaeology from an early age, which led him to pursue a career 
    as an archaeologist specializing in medieval European history. 
    He completed his education at the University of Oxford, where 
    he earned a degree in Archaeology and History.    
    \"\"\"; 
// 调用extract方法,提取信息并返回Person对象
Person person = personExtractor.extract(inputText); 
// 打印Person对象,查看提取的信息
System.out.println(person); 

运行这段代码后,程序输出的内容里,会看到输入的提示自动包含了根据Person记录生成的期望JSON格式。同时,在日志里还能看到详细的请求和响应信息,方便我们了解程序和LLM之间的“交流”过程。

三、处理响应解析错误

不是所有的LLM模型都能乖乖按照我们的要求返回严格的JSON格式响应。比如说,Gemini模型就喜欢把JSON内容包在Markdown代码块里。这样一来,当程序在后台把JSON转换成Java对象(POJO)时,就会出错。

遇到这种情况,我们有几种解决办法。第一种方法是,先把响应内容当成普通字符串提取出来,而不是直接转换成Person记录,然后把那些多余的Markdown标记或者其他不需要的格式去掉。代码示例如下:

// 假设response是从模型返回的原始响应字符串
String cleanedResponse = response.replaceAll(\"^```json\\n|```$\", \"\"); 
// 去掉Markdown代码块标记
Person person = objectMapper.readValue(cleanedResponse, Person.class); 

还有一种方法,我们可以试着把模型的temperature(温度系数)和topK这两个参数的值调低。这两个参数就像是模型的“小开关”,调低它们的值,像Gemini这样的模型生成的输出就会更“听话”,更严格地按照我们的指令来,也就是提高输出的确定性。不过要注意,把这两个参数调低,模型生成多样化、有创意的响应的能力就会变弱。所以,当严格遵循指令比创意更重要的时候,我们才选择这种方法。

四、完整示例代码

下面是一个完整的示例代码,展示了如何严格按照JSON格式获取模型响应,并把响应内容填充到模型POJO里。这个示例主要是为了帮助大家快速上手,实际开发中,大家要记得把代码写得更模块化,做好代码的打包和日志记录工作。

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiChatModelName;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

// 标记这是一个Spring Boot应用的主配置类
@SpringBootApplication 
public class StructuredResponseApplication {
    // 应用程序的入口方法,启动Spring Boot应用
    public static void main(String[] args) { 
        SpringApplication.run(StructuredResponseApplication.class);
    }

    // 通过注解从配置文件中获取OpenAI API密钥
    @Value(\"${OPENAI_API_KEY}\") 
    private String OPENAI_API_KEY;

    // 定义Person记录,包含姓名、年龄、国家和城市字段
    record Person(String name, int age, String country, String city) {} 

    // 定义PersonExtractor接口,用于从文本中提取Person信息
    interface PersonExtractor {
        @UserMessage(\"\"\"
            Extract the name, age. city and country of the person described below.
            Return only JSON, without any markdown markup surrounding it.
            Here is the document describing the person:
            ---
            {{it}}
            \"\"\")
        Person extract(String text);
    }

    // 定义一个Bean,用于在应用启动时执行特定逻辑
    @Bean(\"structuredResponseApplicationRunner\") 
    ApplicationRunner applicationRunner() {
        return args -> {
            // 创建OpenAI聊天语言模型实例,配置API密钥等参数
            ChatLanguageModel model = OpenAiChatModel.builder()
                  .apiKey(OPENAI_API_KEY)
                  .modelName(OpenAiChatModelName.GPT_3_5_TURBO)
                  .logRequests(true)
                  .logResponses(true)
                  .build();

            // 创建PersonExtractor接口的代理对象
            PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, model);

            // 定义输入文本,描述一个人的信息
            String inputText = \"\"\"
                Charles Brown, aged 56, resides in the United Kingdom. Originally 
                from a small town in Devon Charles developed a passion for history 
                and archaeology from an early age, which led him to pursue a career 
                as an archaeologist specializing in medieval European history. 
                He completed his education at the University of Oxford, where 
                he earned a degree in Archaeology and History.    
                \"\"\";

            // 调用extract方法提取信息,得到Person对象
            Person person = personExtractor.extract(inputText);

            // 打印Person对象,查看提取的信息
            System.out.println(person); 
        };
    }
}

五、总结

在LangChain4j中,使用AiServices这种方式和强类型对象打交道,好处可不少。它让我们不用直接和LLM进行复杂的交互,而是通过像PersonExtractorPerson这样具体的类来操作。这样一来,日常开发时,我们就不用太操心和LLM交互的那些复杂细节,能把更多精力放在业务逻辑上,开发效率也能大大提高。希望大家通过这篇文章,对LangChain4j的结构化JSON输出有更深入的理解,在实际项目中能灵活运用!如果在学习过程中有什么问题,欢迎随时交流。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/10268.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号