一、types of chatmessage

在 LangChain4j 中,ChatMessage 用于表示聊天交互中的消息,ChatMessage 存在多种类型,这些类型有助于明确消息的来源和性质,方便在构建对话系统、进行大模型交互时对不同消息进行针对性处理。以下是详细介绍:

1.1用户消息(USER

  • 对应类UserMessage.class

  • 说明:代表用户发送给 AI 或对话系统的消息,是用户表达需求、提问、陈述观点等的载体。在实际应用中,比如用户向客服机器人咨询产品信息,输入的 “这款手机的电池续航能力怎么样?” 这样的文本内容,就会被封装为 UserMessage 类型的消息。

1.2AI 消息(AI

  • 对应类AiMessage.class

  • 说明:是 AI 针对用户消息或其他输入生成的回复内容。当 AI 接收到用户消息后,经过推理和计算返回的回答,像 “这款手机配备了大容量电池,正常使用情况下续航可以达到一整天” 这样的回复,会被标识为 AiMessage 类型。

1.3系统消息(SYSTEM

  • 对应类SystemMessage.class

  • 说明:通常由开发者设定,用于给 AI 提供指令、背景信息或约束条件,影响 AI 的回复策略和风格。例如,在开发一个智能写作助手时,开发者可以发送一条系统消息 “你需要以正式、专业的口吻进行回复,并且内容要简洁明了”,AI 会根据这个系统消息的要求来组织回复内容。

1.4工具执行结果消息(TOOL_EXECUTION_RESULT

  • 对应类ToolExecutionResultMessage.class

  • 说明:当 AI 调用外部工具(如搜索引擎、计算器、文件读取工具等)来辅助完成任务时,工具执行后返回的结果会封装成这种类型的消息。比如在一个知识问答场景中,AI 为了获取更准确的信息调用了搜索引擎工具,搜索引擎返回的搜索结果就会以 ToolExecutionResultMessage 的形式呈现,然后 AI 再基于这些结果生成最终回复。

1.5自定义消息(CUSTOM

  • 对应类CustomMessage.class

  • 说明:用于满足特定应用场景下的个性化消息需求。如果开发者有一些不属于上述标准类型的消息,就可以使用自定义消息类型。比如在一个企业内部的对话系统中,需要添加一种表示权限验证结果的消息类型,就可以将其定义为自定义消息 。

  • 目前只有 ollama 支持


通过对这些不同类型的 ChatMessage 进行管理和处理,LangChain4j 能够更灵活、高效地构建各种复杂的对话应用,适应多样化的业务场景和用户需求。

二、langchain4j中的 types-of-chatmessage 能做什么

在 LangChain4j 中,types-of-chatmessage 即各类聊天消息类型,它们在构建对话系统、实现大语言模型交互等方面发挥着重要作用,具体功能和用途如下:

2.1引导 AI 回复风格与策略

  • 系统消息(SYSTEM:开发者可通过系统消息设定 AI 的行为准则、回复风格及背景信息等,引导 AI 按照特定要求进行回复。例如在开发一个旅游咨询助手时,发送系统消息 “你要以热情友好的风格为用户推荐旅游景点,且优先推荐自然风光类景点” ,AI 就会根据这一设定组织回复内容,在回复用户咨询时,更倾向于介绍自然风光景点,并且语气热情亲切。

  • 用户消息(USER:用户消息明确了用户的需求和问题,AI 会基于用户消息进行分析理解,从而生成针对性的回复。比如用户询问 “去黄山旅游需要准备什么物品”,AI 接收到该用户消息后,会围绕去黄山旅游的准备事项进行作答。

2.2实现多轮对话上下文管理

  • 记忆缓存机制配合:在多轮对话中,不同类型的消息可以被记忆缓存机制记录和管理。例如,USER 消息和 AI 消息会被持续记录,当用户后续的提问依赖之前的对话内容时,系统可以根据记忆缓存中的消息,让 AI 理解对话的上下文,做出连贯、合理的回复。比如用户先询问 “某部电影的主演有谁”,接着问 “其中一位主演还演过什么其他电影” ,AI 能结合上一轮对话,理解用户询问的是上一轮提到的某主演,从而准确作答。

  • 识别对话主体与流向:通过区分消息类型,系统可以清晰地梳理对话的主体和流向。例如在一个复杂的客服对话场景中,通过识别 USER 消息和 AI 消息,能够准确记录用户的诉求和 AI 的处理进展,方便客服人员后续查看对话记录时,快速了解整个对话的过程和问题解决情况。

2.3支持工具调用与结果整合

  • 工具执行结果消息(TOOL_EXECUTION_RESULT:在一些场景中,AI 需要调用外部工具来获取信息或完成特定任务。比如在开发一个天气查询助手时,AI 调用天气查询工具获取实时天气数据,工具返回的结果以 TOOL_EXECUTION_RESULT 类型消息呈现,然后 AI 可以对这些结果进行整合处理,以合适的方式回复用户。

  • 增强 AI 功能实现:工具执行结果消息类型使得 AI 能够借助外部工具的能力,拓展自身功能。例如在一个智能写作辅助系统中,AI 可以调用语法检查工具,工具执行结果消息将语法检查的结果反馈给 AI,AI 再根据这些结果为用户提供写作建议,提升写作质量。

2.4满足个性化定制需求

  • 自定义消息(CUSTOM:当标准的消息类型无法满足特定业务场景需求时,自定义消息类型提供了灵活的扩展方式。比如在一个医疗问诊对话系统中,可能需要添加一种消息类型来表示患者的检查报告数据,开发者可以将其定义为自定义消息,然后按照业务逻辑对该消息进行处理和传递,以实现更贴合实际需求的功能。

2.5辅助开发调试与日志记录

  • 消息类型标识:在开发过程中,不同的消息类型标识有助于开发者快速定位和理解消息的来源与用途。例如在调试对话系统时,如果发现 AI 的回复不符合预期,开发者可以通过查看消息类型,检查系统消息是否正确设置,用户消息是否被准确理解等。

  • 日志分析:在系统运行过程中,记录不同类型的消息有助于进行日志分析。通过分析各类消息的数量、频率以及交互情况,开发者可以了解用户的使用习惯、系统的运行状态,从而对系统进行优化和改进。

一句话总结:可以做角色设定从而塑造 AI 助手的专业身份,打造专业的、限定能力范围和边界的 AI 助手。

三、Prompt的演化

Prompt(提示词)在自然语言处理和大语言模型应用中起着关键作用,从简单纯字符串提问到占位符,再到多角色消息的演变,反映了人们对更灵活、更高效、更贴近真实交互场景的追求,以下是详细的演化历程:

3,1简单纯字符串提问问题阶段

  • 背景:在早期自然语言处理应用和大语言模型发展的初级阶段,人们与模型交互的方式较为直接。当时的模型能力相对有限,开发者和用户主要关注如何让模型理解并回答简单的问题。

  • 形式:直接将问题以纯文本字符串的形式输入给模型,例如 “苹果有哪些营养价值?” “唐朝的开国皇帝是谁?” 这种简单直接的提问方式,没有复杂的结构和设计。

  • 优点:易于理解和使用,对于一些简单、明确的信息查询需求,能够快速得到答案。无论是普通用户还是开发者,都不需要额外学习复杂的提示构造技巧。

  • 局限性:灵活性和复用性差。如果需要针对不同的主体进行类似的提问,比如从问苹果的营养价值改为问香蕉的营养价值,就需要重新输入整个问题。而且,对于复杂的任务,如需要模型按照特定格式输出、结合多个条件进行回答等,纯字符串提问难以满足要求。

3.2占位符阶段

  • 背景:随着应用场景的逐渐丰富,人们希望提示词能够更具通用性和灵活性,减少重复输入,于是占位符的概念应运而生。

  • 形式:在提示词中引入占位符,通常用特定的符号(如 {}[] 等)来表示可变的部分。例如,对于查询水果营养价值的问题,提示词可以设计为 “请告诉我 {水果名称} 的营养价值” 。在实际使用时,只需要将占位符替换为具体的水果名称,如 “苹果” “香蕉” 等,就可以生成新的提示词。

  • 优点:大大提高了提示词的复用性和灵活性。开发者可以预先设计好提示词模板,通过替换占位符的内容,快速生成针对不同对象的提示词,节省了时间和精力。同时,也便于对提示词进行管理和维护。

  • 局限性:虽然占位符提升了灵活性,但主要还是聚焦于单一角色(通常是用户)的提问。对于一些需要模拟多方交互、明确角色身份和行为的复杂场景,如角色扮演、多主体对话等,占位符提示词就显得力不从心。此外,它对于模型的行为约束和引导能力相对有限,难以让模型按照更细致的要求进行回复。

3.3多角色消息阶段

  • 背景:随着大语言模型在对话系统、虚拟交互等领域的广泛应用,人们希望模型能够更好地模拟真实世界中的多方交互场景,更准确地理解不同角色的意图和行为,多角色消息的提示方式便发展起来。

  • 形式:以消息对象的形式组织提示词,明确区分不同的角色,如用户(USER)、系统(SYSTEM)、AI 助手(AI)等。例如在一个客服对话场景中,提示词可以是:

[
    {"role": "SYSTEM", "content": "你是一个专业的电子产品客服,要礼貌、清晰地回答用户的问题"},
    {"role": "USER", "content": "我买的手机充电很慢,怎么办?"}
]


系统角色的消息用于给 AI 设定行为准则和背景信息,用户角色的消息则是具体的提问。

  • 优点

    • 模拟真实交互:能够高度模拟现实生活中的多方对话场景,使模型更好地理解不同角色的立场和需求,从而生成更符合角色特点和场景要求的回复。

    • 精细控制模型行为:通过系统消息,开发者可以详细地告知模型任务目标、回复风格、格式要求等,对模型的输出进行更精细的控制。

    • 支持复杂任务:适用于各种复杂的应用场景,如角色扮演游戏、智能客服、多主体协作模拟等,极大地拓展了大语言模型的应用范围。

  • 局限性:相较于前两种方式,多角色消息的构建相对复杂,需要开发者对交互场景和模型能力有更深入的理解,同时也增加了提示词编写和调试的难度。此外,不同模型对于多角色消息的支持程度和解析方式可能存在差异,需要进行适配。

四、从多角色消息看 SpringAI VS Langchain4j

4.1多角色消息的支持方式

  • SpringAI:SpringAI 是一个基于 Spring Boot 的人工智能开发框架,它提供了与多种大语言模型集成的能力。在处理多角色消息方面,SpringAI 本身没有像 LangChain4j 那样直接内置一套特定的多角色消息数据结构。但借助 Spring 框架强大的依赖注入、配置管理等特性,可以较为方便地整合第三方库来实现多角色消息的处理。例如,开发者可以自定义消息类,明确区分用户消息、系统消息、AI 回复消息等不同角色,然后在与大语言模型交互时,按照模型要求的格式(如 OpenAI 的 API 要求的消息列表格式)进行组装和发送 。

  • LangChain4j:LangChain4j 对多角色消息有着较为明确和直接的支持。它定义了诸如 ChatMessageType 枚举,包含 USER(用户消息)、SYSTEM(系统消息)、AI(AI 回复消息)、TOOL_EXECUTION_RESULT(工具执行结果消息)、CUSTOM(自定义消息)等类型 。通过这些预定义的类型,开发者可以很方便地构建包含多角色消息的对话序列。在与大模型进行交互时,能够清晰地组织和传递不同角色的消息,使模型更好地理解对话上下文和不同角色的意图 。

4.2灵活性与扩展性

  • SpringAI:由于 SpringAI 基于 Spring 生态,其灵活性主要体现在可以利用 Spring 的各种扩展机制。开发者可以根据具体需求,灵活地选择和切换不同的大语言模型服务提供商,并且针对不同模型对多角色消息的不同要求,通过编写自定义的转换器、处理器等组件来适配。同时,SpringAI 还可以与其他 Spring 相关的模块(如数据访问层、安全模块等)进行深度集成,以满足复杂业务场景下对多角色消息处理的扩展需求,例如在多用户对话场景中结合权限管理来控制不同用户的消息角色。但这种灵活性在一定程度上依赖于开发者对 Spring 框架和相关技术的掌握程度。

  • LangChain4j:LangChain4j 提供了一系列的工具和组件来处理多角色消息,具有较好的扩展性。例如,在处理多角色对话时,它的记忆模块(如 MessageWindowChatMemory 和 TokenWindowChatMemory )可以很好地与多角色消息配合,实现对话历史的管理,方便模型在多轮对话中利用不同角色的历史消息进行推理。此外,LangChain4j 还支持工具调用,工具执行结果消息类型使得在多角色交互场景中,能够轻松引入外部工具的输出,丰富对话内容和功能。不过,相较于 SpringAI 基于整个 Spring 生态的扩展性,LangChain4j 的扩展主要围绕大语言模型交互和相关的对话处理功能展开。

4,3易用性

  • SpringAI:对于熟悉 Spring 框架的开发者来说,SpringAI 具有一定的易用性,因为可以利用已有的 Spring 开发经验和知识体系。但是,如果开发者主要关注大语言模型的多角色消息处理,而对 Spring 框架不太熟悉,那么需要花费一定的时间学习 Spring 的相关概念和使用方式,才能顺利地在 SpringAI 中实现多角色消息的处理。此外,由于 SpringAI 没有直接内置多角色消息处理的特定结构,开发者需要自行编写更多的代码来组织和管理不同角色的消息。

  • LangChain4j:LangChain4j 针对大语言模型的多角色消息处理进行了专门设计,提供了直观的接口和数据结构。开发者可以快速上手,按照其定义的方式构建多角色对话。例如,通过简单的代码就可以创建不同角色的消息对象,并将它们组合成对话序列传递给模型。同时,其文档和示例也围绕多角色对话等常见场景展开,对于专注于大语言模型应用开发的人员来说,更容易理解和使用,能够更高效地实现多角色消息相关的功能。

4.4应用场景适配性

  • SpringAI:由于其与 Spring 生态的紧密结合,SpringAI 更适合于已经采用 Spring 框架构建的企业级应用,在这些应用中集成大语言模型的多角色对话功能,例如在企业内部的客服系统、办公自动化系统等场景中,可以方便地与现有的业务逻辑、数据存储、安全认证等模块进行整合。

  • LangChain4j:更侧重于快速构建各种大语言模型驱动的应用,无论是简单的聊天机器人,还是复杂的多步骤任务自动化应用,都能很好地支持。在需要频繁处理多角色对话、进行工具调用和对话记忆管理的场景中,如智能助手、教育辅导应用等,LangChain4j 凭借其对多角色消息的良好支持和相关配套功能,能够更高效地满足开发需求。

五、撸代码

5.1 @SystemMessage+@UserMessage+@V

在 LangChain4j 中,@SystemMessage@UserMessage 和 @V 是用于构建与大语言模型(LLM)交互的注解,主要作用如下:

1. @SystemMessage

  • 作用:定义「系统提示」,用于告知 AI 模型的角色、行为准则、任务背景或具体指令。

  • 特点:通常在对话开始时传递,会影响模型的整体响应风格和行为逻辑。

  • 示例

    @SystemMessage("你是一个专业的Java开发助手,只回答编程相关问题。")
    public interface JavaAssistant {
        // 方法定义...
    }
    

2. @UserMessage

  • 作用:定义「用户输入的提示模板」,用于动态接收用户的具体查询或请求。

  • 特点:可以包含变量(需配合 @V 使用),运行时会被实际用户输入替换。

  • 示例

    @UserMessage("请解释一下什么是:{concept}")
    String explainConcept(@V("concept") String concept);
    


    这里的 {concept} 是变量占位符,会被 @V("concept") 注解的参数值替换。

3. @V(Value 的缩写)

  • 作用:标记方法参数,用于将参数值注入到 @UserMessage 或 @SystemMessage 的模板变量中。

  • 特点:参数名需与模板中的变量名(如 {variable})对应,实现动态内容替换。

  • 示例

    @UserMessage("计算 {a} + {b} 的结果")
    String calculateSum(@V("a") int a, @V("b") int b);
    


    调用 calculateSum(2, 3) 时,模板会被替换为 计算 2 + 3 的结果 并发送给模型

这三个注解配合使用,可简洁地构建「系统提示 + 用户动态输入」的对话模板,让 LLM 交互更符合特定场景需求。@SystemMessage 定义全局规则,@UserMessage 定义用户输入模板,@V 负责动态参数注入,共同实现灵活的 prompt 构建。

step1

在前章工程的基础上新建一个LawAssistant接口,代码如下:

package com.xxx.demo.service;

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;


public interface LawAssistant {
    //案例1
    @SystemMessage("你是一位专业的中国法律顾问,只回答与中国法律相关的问题。" +
            "输出限制:对于其他领域的问题禁止回答,直接返回'抱歉,我只能回答中国法律相关的问题。'")
    @UserMessage("请回答以下法律问题:{{question}},字数控制在{{length}}以内")
    String chat(@V("question") String question, @V("length") int length);
}

step2

写一个controller调用一下大模型(qwen-long)看效果

package com.xxx.demo.controller;

import cn.hutool.core.date.DateUtil;
import com.bbchat.demo.service.LawAssistant;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@Slf4j
public class ChatPromptController {
    @Resource
    private LawAssistant lawAssistant;

    // http://localhost:9009/chatprompt/test1
    @GetMapping(value = "/chatprompt/test1")
    public String test1()
    {
		//故意给个错别字试试
        String chat = lawAssistant.chat("什么是敬业协议?",2000);
        System.out.println(chat);

        String chat2 = lawAssistant.chat("什么是langchain?",2000);
        System.out.println(chat2);

        String chat3 = lawAssistant.chat("介绍下卡布达与金龟次郎",2000);
        System.out.println(chat3);

        String chat4 = lawAssistant.chat("介绍下丁达尔报应",2000);
        System.out.println(chat4);

        return "success : "+ DateUtil.now()+"<br> \n\n chat: "+chat+"<br> \n\n chat2: "+chat2;
    }
}

step3

效果如下(甚至能纠正一下错别字然后给出相关的答案):

5.2 @StructuredPrompt

在 LangChain4j 中,@StructuredPrompt 是用于构建结构化提示词的注解,主要作用是帮助开发者更规范、更灵活地定义与大语言模型(LLM)交互的提示模板,尤其适用于需要固定格式或复杂结构的场景。


其核心特点和用途如下:

  1. 结构化模板定义
    允许将提示词拆分为「系统指令」和「用户输入」两部分,通过注解参数分别指定,替代了单独使用 @SystemMessage 和 @UserMessage 的组合方式,使代码更紧凑。

    示例:

    @StructuredPrompt(
        system = "你是一个数学老师,擅长用简单语言解释公式",
        user = "请解释公式:{formula}"
    )
    public interface MathTeacher {
        String explainFormula(@V("formula") String formula);
    }
    
  2. 支持动态参数注入
    与 @V 注解配合,可在提示模板中嵌入变量(如 {formula}),运行时自动替换为方法参数的值,实现动态生成提示词。

  3. 提升代码可读性
    将系统提示和用户提示集中在一个注解中,避免了多个注解分散定义的情况,使提示词的结构更清晰,便于维护。

  4. 适用于复杂交互场景
    当需要固定格式的系统指令(如角色定义、输出格式约束),同时又要接收动态用户输入时,@StructuredPrompt 能更高效地组织提示内容。

总之,@StructuredPrompt 是对 @SystemMessage + @UserMessage 组合的封装,通过结构化方式简化提示词定义,特别适合需要规范提示格式的场景。

step1

新建一个带@StructuredPrompt的实体类

package com.xxx.demo.entities;

import dev.langchain4j.model.input.structured.StructuredPrompt;
import lombok.Data;


@Data
@StructuredPrompt("根据中国{{legal}}法律,解答以下问题:{{question}}")
public class LawPrompt {
    private String legal;
    private String question;
}

step2

在service层的LawAssistant拓展以下内容

    //案例2
    @SystemMessage("你是一位专业的中国法律顾问,只回答与中国法律相关的问题。" +
            "输出限制:对于其他领域的问题禁止回答,直接返回'抱歉,我只能回答中国法律相关的问题。'")
    String chat(LawPrompt lawPrompt);

step3

在controller中拓展以下方法

    /**
     * TRIPS协议(与贸易有关的知识产权协议):
     * 这是世界贸易组织(WTO)成员间的一个重要协议,
     * 它规定了最低标准的知识产权保护要求,并适用于所有WTO成员。
     * @return
     */
    @GetMapping(value = "/chatprompt/test2")
    public String test2()
    {
        LawPrompt prompt = new LawPrompt();

        prompt.setLegal("竞业协议");
        prompt.setQuestion("TRIPS协议?");

        String chat = lawAssistant.chat(prompt);

        System.out.println(chat);

        return "success : "+ DateUtil.now()+"<br> \n\n chat: "+chat;
    }

step4

看下效果

5.3 PromptTemplate和Prompt对象

在 LangChain4j 中,PromptTemplatePrompt是处理提示词的核心组件,用于构建和管理与大语言模型 (LLM) 交互的输入内容,它们在简化提示词构建、参数化处理和提升开发效率方面发挥重要作用。

1. PromptTemplate(提示模板)

PromptTemplate是一个包含占位符的模板字符串,用于动态生成具体的提示内容。它的主要作用是:

  • 参数化提示词:通过占位符定义可变部分,避免硬编码,提高提示词的复用性

  • 结构化提示构建:规范提示词格式,确保输入到 LLM 的内容符合预期结构

  • 动态内容生成:根据不同场景或输入数据,自动填充占位符生成个性化提示

例如,一个简单的 PromptTemplate 可以是:

PromptTemplate template = PromptTemplate.from("请总结以下内容:{text}");


其中{text}是占位符,后续可以通过传入不同文本内容生成具体提示。
使用时通过withVariables()方法填充参数:

Prompt prompt = template.withVariables("text", "这是需要总结的具体内容...");

2. Prompt(提示对象)

Prompt是由PromptTemplate填充参数后生成的最终对象,代表将要发送给 LLM 的完整提示内容。它的主要作用是:

  • 封装完整提示信息:包含最终要发送给模型的文本内容

  • 提供元数据支持:可以附加模型参数(如温度、最大 tokens 等)

  • 作为模型输入载体:是与 LLM 交互时的标准输入格式


例如,通过 PromptTemplate 生成 Prompt 后,可直接用于模型调用:

ChatLanguageModel model = ...; // 初始化语言模型
Response<String> response = model.generate(prompt.text());

两者关系与典型工作流程

  1. 定义PromptTemplate:创建包含占位符的模板

  2. 填充变量:使用具体值替换模板中的占位符,生成Prompt

  3. 模型交互:将Prompt作为输入传递给 LLM,获取响应


这种设计模式特别适合处理需要动态变化的提示场景,如问答系统、文本生成、信息提取等,能够显著提高代码的可维护性和扩展性。

step1

在controller增加一个方法

    /**
     * @Description: 单个参数可以使用{{it}》”占位符或者”{{参数名}”,如果为其他字符,系统不能自动识别会报错。
     */
    @GetMapping(value = "/chatprompt/test3")
    public String test3()
    {
        // 默认 PromptTemplate 构造使用 it 属性作为默认占位符

        String role = "中医";
        String question = "腰疼";
        

        //1 构造PromptTemplate模板
        PromptTemplate template = PromptTemplate.from("你是一个{{it}}助手,{{question}}怎么办");
        //2 由PromptTemplate生成Prompt
        Prompt prompt = template.apply(Map.of("it",role,"question",question));
        //3 Prompt提示词变成UserMessage
        UserMessage userMessage = prompt.toUserMessage();
        //4 调用大模型
        ChatResponse chatResponse = chatModel.chat(userMessage);

        //4.1 后台打印
        System.out.println(chatResponse.aiMessage().text());
        //4.2 前台返回
        return "success : "+ DateUtil.now()+"<br> \n\n chat: "+chatResponse.aiMessage().text();
    }

step2

看看效果

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐