LangChain4j从入门到精通-3-聊天与语言模型

LangChain4j从入门到精通-3-聊天与语言模型

本文深入解析了LangChain4j框架中与大型语言模型(LLM)交互的核心底层API------ChatModel。作为渐趋淘汰的LanguageModel的现代化替代方案,ChatModel支持更复杂的多轮对话场景,通过接受多个ChatMessage作为输入并返回AiMessage,为开发者提供了更强大的交互能力。文章系统介绍了五种核心ChatMessage类型(UserMessage、AiMessage、SystemMessage等)及其应用场景,并通过丰富Java代码示例演示了如何进行多轮对话、支持多模态内容(如图片、音频、PDF文件)。此外,详细阐述了如何利用ChatRequest进行深度参数定制以精细控制模型行为,并特别介绍了为Kotlin开发者提供的协程异步扩展(chatAsync),有效提升应用响应能力。掌握ChatModel这一基础而关键的API,是开发者高效、灵活集成AI能力,构建智能对话应用的坚实第一步。

#Java #人工智能 #LangChain4j #大模型应用开发 #AI集成

目前有两种类型的LLM API可用:

  • LanguageModel语言模型。它们的API非常简单------接收一个字符串作为输入,并返回一个字符串作为输出。 这个API现在正逐渐被聊天API(第二种API类型)所取代
  • ChatModel. 这些功能接受多个ChatMessage作为输入,并返回一个AiMessage作为输出。 ChatMessage通常包含文本,但一些大语言模型也支持其他模态(如图像、音频等)。
    这类聊天模型的例子包括OpenAI的gpt-4o-mini和谷歌的gemini-1.5-pro。
    LangChain4j将不再扩展对LanguageModel的支持, 因此在所有新功能中,我们将使用ChatModelAPI。
    ChatModel是 LangChain4j 中与大型语言模型交互的低级 API,提供最强大的功能和灵活性。 还有一个高级 API(AI 服务),我们将在介绍完基础知识后详细讲解。

除了 ChatModel和 LanguageModel之外,LangChain4j 还支持以下类型的模型:

  • EmbeddingModel - 该模型可将文本转换为嵌入向量.
  • ImageModel - 该模型可生成和编辑图像.
  • ModerationModel - 该模型可检测文本是否包含有害内容.
  • ScoringModel -该模型可根据查询对多段文本进行评分(或排序)
    本质上决定了每段文本与查询的相关性。这对于RAG非常有用。这些内容将在后面进行介绍。
    现在,让我们更详细地了解一下 ChatModelAPI。
java 复制代码
public interface ChatModel {

    String chat(String userMessage);
    
    ...
}

如你所见,这里有一个简单的chat方法,它接收一个String作为输入并返回一个String作为输出,类似于LanguageModel。 这只是一个便捷方法,让你可以快速轻松地进行尝试,而无需将String包装在UserMessage中。

以下是其他聊天API方法:

java 复制代码
    ...
    
    ChatResponse chat(ChatMessage... messages);

    ChatResponse chat(List<ChatMessage> messages);
        
    ...

这些版本的 chat方法接收一个或多个 ChatMessage作为输入。 ChatMessage是一个表示聊天消息的基础接口。 下一节将详细介绍聊天消息的相关内容。

如果你想自定义请求(例如指定模型名称、温度参数、工具、JSON模式等),可以使用chat(ChatRequest)方法:

java 复制代码
    ...
    
    ChatResponse chat(ChatRequest chatRequest);
        
    ...
java 复制代码
ChatRequest chatRequest = ChatRequest.builder()
    .messages(...)
    .modelName(...)
    .temperature(...)
    .topP(...)
    .topK(...)
    .frequencyPenalty(...)
    .presencePenalty(...)
    .maxOutputTokens(...)
    .stopSequences(...)
    .toolSpecifications(...)
    .toolChoice(...)
    .responseFormat(...)
    .parameters(...) // you can also set common or provider-specific parameters all at once
    .build();

ChatResponse chatResponse = chatModel.chat(chatRequest);

ChatMessage的类型

目前有五种聊天消息类型,每种对应消息的一个"来源":

  • UserMessage: 这是来自用户的一条消息。 用户可以是您应用程序的最终用户(人类),也可以是应用程序本身。 它可能包含:

    • contents(): 消息的内容。根据LLM支持的模态,它可以仅包含单一文本 (String),
    • name(): 用户名。并非所有模型提供商都支持此功能。
    • attributes(): 附加属性:这些属性不会发送给模型,但它们会存储在 ChatMemory中。
  • AiMessage: 这是一条由AI生成的消息,用于回复已发送的消息。 它可能包含:

    • text(): 文本内容
    • thinking(): 思考/推理内容
    • toolExecutionRequests(): 请求执行工具。我们将在另一章节中
    • attributes(): 附加属性,通常是特定于供应商的
  • ToolExecutionResultMessage: 这个是 ToolExecutionRequest的执行结果.

  • SystemMessage: 这是来自系统的消息。 通常,作为开发者,您应该定义此消息的内容。 一般情况下,您会在这里写明关于LLM在此对话中的角色说明, 它应该如何表现,以何种风格回答等等。 LLM的训练使其对SystemMessage的关注度高于其他类型的消息, 因此请谨慎处理,最好不要让终端用户自由定义或向SystemMessage注入某些输入。 通常,它位于对话的开头。

  • CustomMessage: 这是一条自定义消息,可以包含任意属性。此消息类型仅能被支持它的ChatModel实现所使用(目前仅限Ollama)。

既然我们已经了解了所有类型的 ChatMessage,接下来看看如何在对话中组合它们。

最简单的场景是,我们可以向 chat方法提供一个 UserMessage的实例。 这与最初版本的 chat方法类似,后者接受一个 String作为输入。 这里的主要区别在于,它现在返回的不是 String,而是 ChatResponse。

除了 AiMessage外,ChatResponse还包含 ChatResponseMetadata。 ChatResponseMetadata包含 TokenUsage,其中记录了输入(即你提供给生成方法的所有 ChatMessage)包含的令牌数、 输出(在 AiMessage中)生成的令牌数以及总数(输入 + 输出)。 你需要这些信息来计算调用 LLM 的成本。 此外,ChatResponseMetadata还包含 FinishReason, 这是一个枚举,列出了生成停止的各种原因。 通常,如果 LLM 自行决定停止生成,原因会是 FinishReason.STOP。

创建 UserMessage有多种方法,具体取决于内容。 最简单的方法是 new UserMessage("Hi")或 UserMessage.from("Hi")。

多个ChatMessage

那么,为什么需要提供多个ChatMessage作为输入,而不是仅一个呢? 这是因为大语言模型本质上是无状态的,意味着它们不会维护对话的状态。 因此,如果你想支持多轮对话,就需要负责管理对话的状态。

假设你想构建一个聊天机器人。想象一下用户与聊天机器人(AI)之间的一段简单多轮对话:

  • 用户: Hello, my name is Klaus
  • AI: Hi Klaus, how can I help you?
  • 用户: What is my name?
  • AI: Klaus

与 ChatModel的交互将会是这样的:

java 复制代码
UserMessage firstUserMessage = UserMessage.from("Hello, my name is Klaus");
AiMessage firstAiMessage = model.chat(firstUserMessage).aiMessage(); // Hi Klaus, how can I help you?
UserMessage secondUserMessage = UserMessage.from("What is my name?");
AiMessage secondAiMessage = model.chat(firstUserMessage, firstAiMessage, secondUserMessage).aiMessage(); // Klaus

如你所见,在第二次调用 chat方法时,我们不仅提供了单一的 secondUserMessage, 还包含了对话中的先前消息。

手动维护和管理这些消息非常繁琐。 因此,我们引入了ChatMemory的概念,这将在下一章节中详细探讨。

多模态

UserMessage 不仅可以包含文本,还可以包含其他类型的内容。
UserMessage 包含 List<Content> contents.
Content 是一个接口,具有以下实现:

  • TextContent
  • ImageContent
  • AudioContent
  • VideoContent
  • PdfFileContent

你可以在这里的对比表中查看哪些LLM提供商支持哪些模式。

这是一个向大语言模型发送文本和图片的示例:

java 复制代码
UserMessage userMessage = UserMessage.from(
    TextContent.from("Describe the following image"),
    ImageContent.from("https://example.com/cat.jpg")
);
ChatResponse response = model.chat(userMessage);
文本内容

TextContent 是最简单的Content形式,代表纯文本并包装单个String。
UserMessage.from(TextContent.from("Hello!"))UserMessage.from("Hello!")是一样的效果.

可以在 UserMessage中提供一个或多个 TextContent:

java 复制代码
UserMessage userMessage = UserMessage.from(
    TextContent.from("Hello!"),
    TextContent.from("How are you?")
);
图片内容

根据LLM提供商的不同,ImageContent既可以从远程图片的URL创建(参见上方示例),也可以从Base64编码的二进制数据创建:

java 复制代码
byte[] imageBytes = readBytes("/home/me/cat.jpg");
String base64Data = Base64.getEncoder().encodeToString(imageBytes);
ImageContent imageContent = ImageContent.from(base64Data, "image/jpg");
UserMessage userMessage = UserMessage.from(imageContent);

还可以指定 DetailLevel枚举(包含 LOW/HIGH/AUTO选项)来控制模型处理图像的方式。 更多详情请参阅此处。

音频内容

AudioContent 类似于 ImageContent,但表示音频内容。

Video Content

VideoContent 类似于 ImageContent,但表示视频内容。

PDF File Content

PdfFileContent 类似于 ImageContent,但表示 PDF 文件的二进制内容。

Kotlin扩展

ChatModel的 Kotlin 扩展提供了异步方法来处理与语言模型的聊天交互,利用了 Kotlin 的 协程功能。chatAsync方法允许非阻塞地处理 ChatRequest或 ChatRequest.Builder配置,返回带有模型回复的 ChatResponse。类似地,generateAsync方法处理聊天消息的异步响应生成。这些扩展简化了在 Kotlin 应用程序中高效构建聊天请求和处理对话的过程。请注意,这些方法标记为实验性,可能会随时间演进。

ChatModel.chatAsync(request: ChatRequest): 专为Kotlin协程设计,这个异步扩展函数将同步的chat方法封装在Dispatchers.IO协程作用域内。这实现了非阻塞操作,对保持应用响应能力至关重要。该函数特意命名为chatAsync以避免与现有同步方法chat冲突。其函数签名为:suspend fun ChatModel.chatAsync(request: ChatRequest): ChatResponse。关键字suspend将其标记为协程函数。

ChatModel.chat(block: ChatRequestBuilder.() -> Unit) : 该版本的chat采用Kotlin类型安全构建器DSL,提供更简洁的实现方式。它在内部使用chatAsync进行异步执行的同时,简化了ChatRequest对象的构建过程。通过协程机制,该版本兼具代码简洁性和非阻塞特性。

相关推荐
NAGNIP10 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab12 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab12 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP15 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年15 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼16 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS16 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区17 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈17 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang18 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx