Java大模型开发入门 (6/15):对话的灵魂 - 深入理解LangChain4j中的模型、提示和解析器

前言

在上一篇文章中,我们见证了@AiService注解的惊人威力。仅仅通过定义一个Java接口,我们就实现了一个功能完备的AI聊天服务。这感觉就像魔法一样!

但作为专业的工程师,我们知道“任何足够先进的技术,都与魔法无异”。今天,我们的任务就是揭开这层魔法的面纱,深入理解其背后支撑我们与AI进行高质量对话的三大支柱:

  1. 语言模型 (Models):AI的“大脑”,我们如何理解并与它交互。
  2. 提示模板 (Prompt Templates):控制我们对AI“说什么”的艺术,实现动态、可复用的指令。
  3. 输出解析器 (Output Parsers):驯服AI的“输出”,让它返回我们需要的Java对象,而不仅仅是文本。

掌握了这三者,你将从一个简单的AI“调用者”,蜕变为一个能精确控制AI行为的“指挥家”。

第一部分:再探模型 (Models) - AI的“大脑”

application.properties中,我们配置了langchain4j.open-ai.chat-model.*等属性,并通过LangChain4jConfig创建了一个ChatLanguageModel类型的Bean。

ChatLanguageModel是LangChain4j中的一个核心接口,它代表了所有聊天式AI模型(如GPT-3.5/4, DeepSeek-Chat)的统一抽象。你可以把它想象成Java JDBC规范中的DataSource接口,它为所有不同厂商的数据库提供了统一的访问标准。

我们配置的OpenAiChatModel是这个接口的一个具体实现。如果我们想换成Google的Gemini模型,只需引入langchain4j-google-vertex-ai依赖,并创建一个VertexAiChatModel的Bean即可,而我们上层的业务代码(如Assistant接口)几乎无需改动。这就是面向接口编程的威力。

核心概念

  • ChatLanguageModel: 用于一次请求-响应的交互。
  • StreamingChatLanguageModel: ChatLanguageModel的子接口,用于流式响应。AI的回答会一个词一个词地“流”回来,能极大地提升用户体验,我们将在后续文章中探讨它。
第二部分:提示模板 (Prompt Templates) - 控制AI的“输入”

在上一篇中,我们使用了简单的assistant.chat(userMessage)。LangChain4j只是将userMessage作为用户提问直接发给了AI。但如果我们想让AI扮演特定角色,或者根据多个动态参数来提问呢?这时就需要提示模板。

在LangChain4j中,我们可以通过@UserMessage注解和{{...}}占位符来创建强大的提示模板。

实战:创建一个食谱生成器

让我们来改造Assistant接口,让它能根据菜系和原料生成食谱。

  1. 修改Assistant接口

    package com.example.aidemoapp.service;import dev.langchain4j.service.SystemMessage;
    import dev.langchain4j.service.UserMessage;
    import dev.langchain4j.service.spring.AiService;@AiService
    public interface Assistant {@SystemMessage("You are a polite assistant")String chat(String userMessage);// 新增一个使用模板的方法@UserMessage("""请创建一个 {{dish_type}} 菜肴的食谱。 主要食材是:{{ingredients}}。 请提供包含标题、简要描述、所需食材列表以及逐步操作说明的完整食谱。""")String createRecipe(@V("dish_type") String dish_type, @V("ingredients") String ingredients);
    }
    

    代码解析

    • 我们在@UserMessage注解中使用了Java 15的文本块(三引号),方便编写多行提示。
    • {{dish_type}}{{ingredients}}是占位符。
    • LangChain4j会自动将createRecipe方法的dish_type参数值填充到{{dish_type}}占位符中,ingredients参数同理。
  2. ChatController中调用新方法

    package com.example.aidemoapp.controller;// ... imports ...@RestController
    @RequestMapping("/api/v2/chat")
    @RequiredArgsConstructor
    public class ChatController {private final Assistant assistant;@GetMappingpublic String chat(@RequestParam("message") String message) {return assistant.chat(message);}@GetMapping("/recipe")public String recipe(@RequestParam String dishType, @RequestParam String ingredients) {return assistant.createRecipe(dishType, ingredients);}
    }
    

现在,当你请求GET /api/v2/chat/recipe?dishType=Sichuan&ingredients=tofu,garlic,chili时,LangChain4j会向AI发送一个被完整填充的、结构化的Prompt,从而得到一个高质量的食谱。

第三部分:输出解析器 (Output Parsers) - 控制AI的“输出”

上面的食谱生成器返回的是一个长字符串。在真实应用中,我们更希望得到一个结构化的Java对象(POJO),以便于在前端展示或进行后续处理。

LangChain4j的AiServices能做到这一点!只要你将方法的返回类型从String改为一个自定义的Java类,LangChain4j就会自动指示AI以JSON格式输出,并自动将JSON反序列化为你的Java对象。这就是输出解析器的魔力!

实战:将食谱输出为Java对象

  1. 创建Recipe POJO
    com.example.aidemoapp下创建dto包,并新建Recipe.java类。

    package com.example.aidemoapp.dto;import lombok.Data;
    import java.util.List;@Data // Lombok注解,自动生成getter, setter, toString等
    public class Recipe {private String title;private String description;private List<String> ingredients;private List<String> instructions;
    }
    
  2. 再次修改Assistant接口
    我们将createRecipe方法的返回类型改为Recipe

    // ... imports ...
    import com.example.aidemoapp.dto.Recipe; // 引入Recipe类@AiService
    public interface Assistant {@SystemMessage("You are a polite assistant")String chat(String userMessage);// 新增一个使用模板的方法@UserMessage("""请创建一个 {{dish_type}} 菜肴的食谱。 主要食材是:{{ingredients}}。 请提供包含标题、简要描述、所需食材列表以及逐步操作说明的完整食谱。""")Recipe createRecipe(@V("dish_type") String dish_type, @V("ingredients") String ingredients);
    }
    

    提示词增强:我们明确在提示中要求AI以JSON格式返回,并描述了JSON的结构。虽然LangChain4j在很多情况下会自动处理,但明确地指示AI可以极大地提高成功率和稳定性。

  3. 修改ChatController

    // ... imports ...
    import com.example.aidemoapp.dto.Recipe;// ...
    public class ChatController {// ... chat和recipe方法 ...@GetMapping("/recipe-object")public Recipe recipeObject(@RequestParam String dishType, @RequestParam String ingredients) {return assistant.createRecipeAsObject(dishType, ingredients);}
    }
    

    由于@RestController的存在,Spring Boot会自动将返回的Recipe对象序列化为JSON字符串,作为HTTP响应返回给前端。

现在,当你请求GET /api/v2/chat/recipe-object?dishType=Italian&ingredients=pasta,tomatoes,basil时,你将直接得到一个干净的、结构化的JSON响应!

源码获取

本文中所有实战代码均已同步更新至Gitee仓库,方便您下载、运行和学习。

源码地址:https://gitee.com/chaocloud/springboot-langchain4j-demo

总结

今天,我们揭开了LangChain4j高效开发的神秘面纱。我们学习了:

  • Models是连接AI大脑的统一接口。
  • Prompt Templates (@UserMessage{{...}}) 让我们能精确地、动态地控制输入
  • Output Parsers (通过改变返回类型) 让我们能将AI的输出从非结构化的文本,转换为结构化的、可被程序直接利用的Java对象。

我们已经从简单的“你问我答”进化到了可以与AI进行可预测、结构化交互的新阶段。但这还不够,我们的AI助手还是“金鱼般的记忆”,每次对话都是一次全新的开始。如何让它记住上下文,进行真正的多轮对话呢?


下一篇预告:
Java大模型开发入门 (7/15):让AI拥有记忆 - 使用LangChain4j实现多轮对话》—— 我们将为我们的Assistant装上“记忆芯片”,探索LangChain4j中的ChatMemory机制,打造一个能真正联系上下文的智能聊天机器人!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/84912.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用Rust如何构建高性能爬虫

习惯了使用Python来写爬虫&#xff0c;如果使用Rust需要有哪些考量&#xff1f; 根据我了解的Rust 在性能、资源效率和并发处理方面完胜 Python&#xff0c;但是 Python 在开发速度和生态成熟度上占优。所以说&#xff0c;具体用那种模式&#xff0c;结合你项目特点做个详细的…

CentOS7报错:Cannot find a valid baseurl for repo: base/7/x86_64

这个错误通常出现在 CentOS/RHEL 7 系统中&#xff0c;当你尝试运行 yum update 或 yum install 时&#xff0c;系统无法连接到默认的软件仓库&#xff08;repository&#xff09;。 可能的原因 网络连接问题&#xff1a;系统无法访问互联网或仓库服务器。错误的仓库配置&…

云平台|Linux部分指令

目录 云平台 操作系统&#xff08;镜像&#xff09; 管理应用实例 远程连接 远程连接工具 linux相关命令&#xff08;重点&#xff09; 云平台 1、阿里云&#xff08;学生免费&#xff0c;不包流量 流量0.8---1G&#xff09; 2、腾讯云&#xff08;抢&#xff09; 3、华…

AI首次自主发现人工生命

转&#xff1a; 近日&#xff0c;人工智能领域迎来了一项革命性的突破。Transformer 论文作者之一的 Llion Jones 与前谷歌研究人员 David Ha 共同创立的人工智能公司 Sakana AI&#xff0c;联合MIT、OpenAI、瑞士AI实验室IDSIA等机构的研究人员&#xff0c;共同提出了一种名为…

Day.31

变量类型&#xff1a; name: str "Alice" age: int 30 height: float 1.75 is_student: bool False 注解&#xff1a; def add(a: int, b: int) -> int: return a b def greet(name: str) -> None: print(f"Hello, {name}") 定义矩形类&a…

光谱数据分析的方法有哪些?

光谱数据分析是通过特征光谱识别物质结构与成分的核心技术&#xff0c;其标准化流程如下&#xff1a; ‌一、数据预处理‌&#xff08;消除干扰噪声&#xff09; ‌去噪平滑‌ Savitzky-Golay滤波&#xff1a;保留光谱特征峰形&#xff0c;消除高频噪声。 移动平均法&#…

RabbitMQ的使用--Spring AMQP(更新中)

1.首先是创建项目 在一个父工程 mq_demo 的基础上建立两个子模块&#xff0c;生产者模块publisher&#xff0c;消费者模块 consumer 创建项目&#xff1a; 建立成功&#xff1a; 删除多余文件 创建子模块1&#xff1a;publisher&#xff08;生产者模块&#xff09; 右键---…

DAY 31 文件的规范拆分和写法

浙大疏锦行 今日的示例代码包含2个部分 notebook文件夹内的ipynb文件&#xff0c;介绍下今天的思路项目文件夹中其他部分&#xff1a;拆分后的信贷项目&#xff0c;学习下如何拆分的&#xff0c;未来你看到的很多大项目都是类似的拆分方法 知识点回顾 规范的文件命名规范的文件…

EtherCAT至TCP/IP异构网络互联:施耐德M580 PLC对接倍福CX5140解决方案

一、项目背景与需求 某智能工厂致力于打造高度自动化的生产流水线&#xff0c;其中部分核心设备采用EtherCAT协议进行通信&#xff0c;以实现高速、高精度的控制&#xff0c;例如基于EtherCAT总线的倍福&#xff08;Beckhoff&#xff09;CX5140PLC&#xff0c;它能够快速响应设…

[学习] FIR多项滤波器的数学原理详解:从多相分解到高效实现(完整仿真代码)

FIR多项滤波器的数学原理详解&#xff1a;从多相分解到高效实现 文章目录 FIR多项滤波器的数学原理详解&#xff1a;从多相分解到高效实现引言一、FIR滤波器基础与多相分解原理1.1 FIR滤波器数学模型1.2 多相分解的数学推导1.3 多相分解的物理意义 二、插值应用中的数学原理2.1…

Java并发编程实战 Day 22:高性能无锁编程技术

【Java并发编程实战 Day 22】高性能无锁编程技术 文章简述 在高并发场景下&#xff0c;传统的锁机制&#xff08;如synchronized、ReentrantLock&#xff09;虽然能够保证线程安全&#xff0c;但在高竞争环境下容易引发性能瓶颈。本文深入探讨无锁编程技术&#xff0c;重点介绍…

打破语言壁垒!DHTMLX Gantt 与 Scheduler 文档正式上线中文等多语言版本!

你还在为英文技术文档望而却步吗&#xff1f;现在好消息来了&#xff01;DHTMLX 团队宣布&#xff0c;其两款明星组件——DHTMLX Gantt&#xff08;甘特图&#xff09;与 DHTMLX Scheduler&#xff08;日程排程器&#xff09;的官方文档&#xff0c;现已全面支持中文、德语、韩…

无监督 vs 有监督的本质区别

一、无监督 vs 有监督的本质区别 1. 无监督学习 定义&#xff1a;数据中没有人为标注的 “正确答案”&#xff08;如类别标签、目标值&#xff09;&#xff0c;模型需自己发现数据中的模式。任务目标&#xff1a;学习数据的分布规律、结构或生成逻辑。例子&#xff1a; 文本续…

【Linux】初见,进程概念

前言&#xff1a; 上文我们讲到了Linux下的第一个程序&#xff1a;进度条 【Linux】LInux下第一个程序&#xff1a;进度条-CSDN博客 本文我们来讲一讲Linux中下一个非常重要的东西&#xff1a;进程 1.冯诺依曼体系结构 我们所见的大部分计算机都是遵循的冯诺依曼体系结构…

Linux进程间通信(IPC)详解:从入门到理解

引言 作为一名C开发初学者&#xff0c;理解Linux下的进程间通信&#xff08;Inter-Process Communication&#xff0c;简称IPC&#xff09;机制是非常重要的一步。本文将用通俗易懂的语言&#xff0c;配合直观的图示&#xff0c;帮助你理解Linux进程间通信的基本概念和各种实现…

SQL进阶之旅 Day 27:存储过程与函数高级应用

【SQL进阶之旅 Day 27】存储过程与函数高级应用 文章简述 在数据库开发中&#xff0c;存储过程和函数是实现复杂业务逻辑、提高代码复用性和提升系统性能的重要工具。本文作为“SQL进阶之旅”系列的第27天&#xff0c;深入探讨存储过程与函数的高级应用&#xff0c;涵盖其设计…

泰国零售巨头 CJ Express 借助 SAP 内存数据库实现高效数据管理

泰国 CJ Express 运用 SAP 内存数据库有效控制数据增长案例 “Datavard Outboard 操作简便、配置轻松&#xff0c;我们得以在生产系统上完成数据归档&#xff0c;成功将约 730GB 数据迁移至 Hadoop 集群。”——K. Jak&#xff0c;J Express 技术服务经理 关于 CJ Express …

ImageSharp.Web 使用指南:高效处理ASP.NET Core中的图像

文章目录 前言一、ImageSharp.Web简介二、安装与配置1. 安装NuGet包2. 基本配置3. 高级配置 三、核心功能与使用示例1. 基本图像处理2. 处理模式详解3. 自定义处理命令 四、缓存策略1. 物理文件系统缓存2. 分布式缓存3. 自定义缓存 五、性能优化建议六、常见问题解决1. 图像处理…

使用R进行数字信号处理:婴儿哭声分析深度解析

音频信号处理将原始声音数据转化为有意义的洞见&#xff0c;适用于语音分析、生物声学和医学诊断等领域。使用R语言&#xff0c;我们可以处理音频文件、可视化频率内容&#xff0c;并生成如声谱图等详细图表。本指南将展示如何使用R包tuneR、seewave和rpanel分析婴儿哭声音频文…

【环境配置】解决linux每次打开终端都需要source .bashrc文件的问题

解决方法&#xff1a; cd vim .bash_profile输入下面内容后 :wq 保存并退出 # .bash_profileif [ -f ~/.bashrc ]; then. ~/.bashrc fi 参考链接&am…