langchain4j 学习系列(6)-结构化输出(参数提取)

继续学习langchain4j,玩过dify的朋友想必对"参数提取器"这个节点很熟悉,示例:

image

 参数提取器可以很方便的从“非结构的自然语言”中,提取出结构化的结果。

image

下面来看看langchain4j如何实现类似功能:

    public static final String TEST_DATA = """
            金庸(1924年3月10日—2018年10月30日),本名查良镛,浙江省海宁市人,祖籍江西省婺源县浙源乡凤山村 [10] [146-147],1948年移居香港。
            当代武侠小说作家、新闻学家、企业家、政治评论家、社会活动家,被誉为“香港四大才子”之一,与古龙、梁羽生、温瑞安并称为“中国武侠小说四大宗师”。 [1-4]
            1944年,考入重庆中央政治大学外交系。1946年秋,进入上海《大公报》任国际电讯翻译。1948年,毕业于上海东吴大学法学院,并被调往《大公报》香港分社 [5]。
            1952年调入《新晚报》编辑副刊,并写出《绝代佳人》《兰花花》等电影剧本。1959年,金庸等人于香港创办《明报》 [6]。
            1985年起,历任香港特别行政区基本法起草委员会委员、政治体制小组负责人之一,基本法咨询委员会执行委员会委员,以及香港特别行政区筹备委员会委员。
            1994年,受聘北京大学名誉教授 [7]。
            2000年,获得大紫荆勋章。2007年,出任香港中文大学文学院荣誉教授 [5]。
            2009年9月,被聘为中国作协第七届全国委员会名誉副主席 [8];同年荣获2008影响世界华人终身成就奖 [9]。2010年,获得剑桥大学哲学博士学位 [2]。
            "2018年10月30日,在香港逝世,享年94岁。
            """;
    
    @GetMapping(value = "/extract", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<String> extract() {
        try {
            Prompt prompt = PromptTemplate.from("请从以下生平介绍中,提取出该人物的基本信息,以json格式输出,参考格式:{{json_output_sample}} \n{{test_data}}")
                    .apply(Map.of("json_output_sample", "{\"name\":\"张三\",\"age\":10,\"birthDay\":\"2000-01-01\",\"isAlive\":false,\"deathDate\":\"2040-02-01\",\"degree\":\"本科\"}",
                            "test_data", TEST_DATA));
            String text = ollamaChatModel.chat(prompt.toUserMessage()).aiMessage().text();
            return ResponseEntity.ok(text);
        } catch (Exception e) {
            log.error("extract", e);
            return ResponseEntity.ok("{\"error\":\"extract error: " + e.getMessage() + "\"}");
        }
    }

代码很简单,直接在prompt提示词里,告诉LLM怎么做就行,输出结果:

image

不过,这个输出结果是个string,还不能算是结构化的输出,可以再改进一下:

    /**
     * Person 记录类,用于表示一个人的基本信息
     * 使用 Java record 类型实现,自动生成构造函数、getter 方法、equals()、hashCode() 和 toString() 方法
     *
     * @param name      人的姓名
     * @param age       人的年龄
     * @param birthDay  人的出生日期
     * @param isAlive   人是否仍然活着
     * @param deathDate 人的死亡日期(如果已故)
     * @param degree    人的学位
     */
    record Person(String name, int age, Date birthDay, boolean isAlive, Date deathDate, String degree) {
        // 这是一个记录类声明,包含了表示个人基本信息的数据字段
        // 所有字段都是 final 的,且自动生成对应的访问器方法
    }

    /**
     * 人员信息提取接口
     * 该接口定义了一个从生平介绍中提取人员信息的方法
     */
    interface PersonExtractor {
        /**
         * 从生平介绍中提取人员主要信息
         *
         * @param biography 人员的生平介绍文本
         * @return 包含提取信息的Person对象
         */
        @SystemMessage("""
                你的任务是从生平介绍中,提取出该人的主要信息:
                name[姓名],age[年龄], birthDay[出生日期], isAlive[是否健在], deathDate[死亡日期(如果已逝世)], degree[最高学历]
                """)
        Person extractPerson(String biography);
    }

    /**
     * 处理GET请求,提取人员信息并以JSON格式返回
     *
     * @return 返回包含提取的人员信息的ResponseEntity对象
     */
    @GetMapping(value = "/extract2", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Person> extract2() {
        try {
            // 创建PersonExtractor实例,使用AiServices和ollamaChatModel
            PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, ollamaChatModel);
            // 使用TEST_DATA调用extractPerson方法提取人员信息
            Person person = personExtractor.extractPerson(TEST_DATA);
            // 返回成功响应,包含提取的人员信息
            return ResponseEntity.ok(person);
        } catch (Exception e) {
            // 捕获异常并记录错误日志
            log.error("extract2", e);
            // 发生异常时返回默认的Person对象
            return ResponseEntity.ok(new Person("", -1, null, false, null, ""));
        }
    }

这里我们用抽象级别更高的AiService来创建指定的抽取器,一步到位的输出了对象实例

image

下面是终端打印的日志:

2025-12-08T19:34:44.508+08:00  INFO 9196 --- [langchain4j-study] [nio-8080-exec-8] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: http://localhost:11434/api/chat
- headers: [Content-Type: application/json]
- body: {
  "model" : "deepseek-v3.1:671b-cloud",
  "messages" : [ {
    "role" : "system",
    "content" : "你的任务是从生平介绍中,提取出该人的主要信息:\nname[姓名],age[年龄], birthDay[出生日期], isAlive[是否健在], deathDate[死亡日期(如果已逝世)], degree[最高学历]\n"
  }, {
    "role" : "user",
    "content" : "金庸(1924年3月10日—2018年10月30日),本名查良镛,浙江省海宁市人,祖籍江西省婺源县浙源乡凤山村 [10] [146-147],1948年移居香港。\n当代武侠小说作家、新闻学家、企业家、政治评论家、社会活动家,被誉为“香港四大才子”之一,与古龙、梁羽生、温瑞安并称为“中国武侠小说四大宗师”。 [1-4]\n1944年,考入重庆中央政治大学外交系。1946年秋,进入上海《大公报》任国际电讯翻译。1948年,毕业于上海东吴大学法学院,并被调往《大公报》香港分社 [5]。\n1952年调入《新晚报》编辑副刊,并写出《绝代佳人》《兰花花》等电影剧本。1959年,金庸等人于香港创办《明报》 [6]。\n1985年起,历任香港特别行政区基本法起草委员会委员、政治体制小组负责人之一,基本法咨询委员会执行委员会委员,以及香港特别行政区筹备委员会委员。\n1994年,受聘北京大学名誉教授 [7]。\n2000年,获得大紫荆勋章。2007年,出任香港中文大学文学院荣誉教授 [5]。\n2009年9月,被聘为中国作协第七届全国委员会名誉副主席 [8];同年荣获2008影响世界华人终身成就奖 [9]。2010年,获得剑桥大学哲学博士学位 [2]。\n\"2018年10月30日,在香港逝世,享年94岁。\n\nYou must answer strictly in the following JSON format: {\n\"name\": (type: string),\n\"age\": (type: integer),\n\"birthDay\": (type: date string (2023-12-31)),\n\"isAlive\": (type: boolean),\n\"deathDate\": (type: date string (2023-12-31)),\n\"degree\": (type: string)\n}"
  } ],
  "options" : {
    "stop" : [ ]
  },
  "stream" : false,
  "tools" : [ ]
}

从user提示词中最后的“\n\nYou must answer strictly in the following JSON format: {\n\"name\": (type: string),\n\"age\": (type: integer),\n\"birthDay\": (type: date string (2023-12-31)),\n\"isAlive\": (type: boolean),\n\"deathDate\": (type: date string (2023-12-31)),\n\"degree\": (type: string)\n” 这段可以看出,langchain4j将Person类的信息,转换成了自描述的schema发给了LLM



本文示例完整代码:GitHub - yjmyzz/langchain4j-study at day06

posted @ 2025-12-08 19:39  菩提树下的杨过  阅读(0)  评论(0)    收藏  举报