LangChain4j 核心抽象:ChatMessage、UserMessage 与模型无关设计

LangChain4j 核心抽象:ChatMessage、UserMessage 与模型无关设计

前言

LangChain4j 的核心设计理念是"模型无关"------通过统一的抽象层,让开发者能够轻松切换不同的 LLM 提供商(OpenAI、Ollama、Anthropic 等),而无需重写业务代码。这一设计的基石就是 ChatMessage 体系。

本文将深入剖析 LangChain4j 的核心抽象,包括消息类型体系、Content 抽象、多模态支持,以及如何实现模型提供商的无缝切换。

一、消息类型体系

1.1 ChatMessage 层级结构

LangChain4j 定义了完整的消息类型继承体系:

复制代码
ChatMessage (抽象基类)
│
├── SystemMessage     # 系统消息
├── UserMessage       # 用户消息
├── AiMessage         # AI 响应消息
├── ToolExecutionResultMessage  # 工具执行结果消息
└── Content ...       # 消息内容抽象

1.2 消息类型详解

1.2.1 SystemMessage(系统消息)

用于定义 AI 的角色和行为规则。

java 复制代码
import dev.langchain4j.data.message.SystemMessage;

// 创建系统消息
SystemMessage systemMessage = SystemMessage.from(
    "你是一个专业的Java技术顾问,擅长解答Spring Boot相关问题"
);

// 在 AI Service 中使用
@SystemMessage("你是一个专业的Java技术顾问")
public interface Assistant {
    String chat(String userMessage);
}

使用场景:

  • 定义 AI 角色(如"你是一个客服助手")
  • 设定输出格式(如"请以JSON格式返回")
  • 限制行为范围(如"只回答技术问题")
1.2.2 UserMessage(用户消息)

表示用户的输入,支持多模态内容(文本 + 图像)。

java 复制代码
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;

// 纯文本消息
UserMessage textMessage = UserMessage.from("什么是Spring Boot?");

// 图文混合消息(1.4.0 新特性)
UserMessage multiModalMessage = UserMessage.from(
    ImageContent.from("https://example.com/screenshot.png"),
    TextContent.from("请解释这张截图中的错误信息")
);

使用场景:

  • 用户提问
  • 多模态输入(文本 + 图像/音频/视频)
  • 历史上下文传递
1.2.3 AiMessage(AI 响应消息)

表示 LLM 的响应内容。

java 复制代码
import dev.langchain4j.data.message.AiMessage;

// 创建 AI 消息
AiMessage aiMessage = AiMessage.from(
    "Spring Boot 是基于 Spring 框架的快速开发框架..."
);

// 获取工具调用(Function Calling)
if (aiMessage.hasToolExecutionRequests()) {
    List<ToolExecutionRequest> requests = aiMessage.toolExecutionRequests();
    for (ToolExecutionRequest request : requests) {
        System.out.println("调用工具: " + request.name());
        System.out.println("参数: " + request.arguments());
    }
}

使用场景:

  • LLM 文本响应
  • 工具调用请求
  • 保存对话历史
1.2.4 ToolExecutionResultMessage(工具执行结果)

表示工具执行后的返回值。

java 复制代码
import dev.langchain4j.data.message.ToolExecutionResultMessage;

// 创建工具执行结果消息
ToolExecutionResultMessage resultMessage = ToolExecutionResultMessage.from(
    "tool-execution-id-123",
    "查询结果:北京今天气温 25°C"
);

// 在对话中使用
List<ChatMessage> messages = new ArrayList<>();
messages.add(UserMessage.from("北京今天天气如何?"));
messages.add(AiMessage.from("tool-execution-id-123", null)); // AI 调用工具
messages.add(resultMessage); // 工具执行结果

String response = model.generate(messages);

使用场景:

  • Function Calling 流程
  • 多步骤工具调用
  • Agent 决策循环

1.3 消息流转图

复制代码
┌─────────────────────────────────────────────────────────────┐
│                   ChatMessage 消息流转                        │
└─────────────────────────────────────────────────────────────┘

用户                    LLM                    工具系统
 │                      │                       │
 │ 1. UserMessage       │                       │
 ├─────────────────────>│                       │
 │   "北京天气?"       │                       │
 │                      │                       │
 │                      │ 2. 生成工具调用请求   │
 │                      ├────────────────────>│
 │                      │   ToolExecutionRequest│
 │                      │                       │
 │                      │ 3. 执行工具           │
 │                      │<─────────────────────┤
 │                      │   返回结果             │
 │                      │                       │
 │                      │ 4. 生成最终响应       │
 │<─────────────────────┤                       │
 │ 5. AiMessage         │                       │
 │   "北京今天25°C"     │                       │
 │                      │                       │
 └──────────────────────┴───────────────────────┘

对话历史保存:
ChatMemory
├── MessageWindowChatMemory(窗口滑动)
└── TokenWindowChatMemory(Token 预算)

二、Content 抽象:多模态支持

2.1 Content 层级结构

LangChain4j 1.4.0 引入了统一的 Content 抽象,支持多模态输入:

复制代码
Content (接口)
│
├── TextContent      # 文本内容
├── ImageContent     # 图像内容(1.4.0 新增)
├── AudioContent     # 音频内容(预留)
└── VideoContent     # 视频内容(预留)

2.2 TextContent(文本内容)

java 复制代码
import dev.langchain4j.data.message.TextContent;

// 创建文本内容
TextContent textContent = TextContent.from("Hello, LangChain4j!");

// UserMessage 支持多个 TextContent
UserMessage message = UserMessage.from(
    TextContent.from("问题1:什么是Spring Boot?"),
    TextContent.from("问题2:如何快速开始?")
);

2.3 ImageContent(图像内容)

1.4.0 新增特性,支持图像输入,适配 GPT-4V、Claude-3V 等视觉模型。

java 复制代码
import dev.langchain4j.data.message.ImageContent;

// 从 URL 创建
ImageContent fromUrl = ImageContent.from("https://example.com/image.jpg");

// 从 Base64 创建
ImageContent fromBase64 = ImageContent.from(
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
);

// 从字节数组创建
byte[] imageBytes = Files.readAllBytes(Paths.get("screenshot.png"));
ImageContent fromBytes = ImageContent.from(imageBytes);

// 图文混合输入
UserMessage visionMessage = UserMessage.from(
    ImageContent.from("https://example.com/code.png"),
    TextContent.from("请解释这段代码的功能")
);

// 调用支持视觉的模型
String response = gpt4VisionModel.generate(visionMessage);

2.4 多模态组合示例

java 复制代码
// 示例:图文混合同步查询
UserMessage complexQuery = UserMessage.from(
    TextContent.from("请分析以下内容:"),
    ImageContent.from("https://example.com/diagram.png"),
    TextContent.from("图表中的数据趋势如何?")
);

String analysis = model.generate(complexQuery);

// 示例:多图对比
UserMessage comparison = UserMessage.from(
    TextContent.from("对比这两张图片的差异:"),
    ImageContent.from("https://example.com/before.png"),
    ImageContent.from("https://example.com/after.png")
);

String diff = model.generate(comparison);

三、模型提供商切换

3.1 统一接口设计

LangChain4j 提供了统一的模型接口,支持无缝切换提供商:

java 复制代码
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.anthropic.AnthropicChatModel;
import dev.langchain4j.model.ollama.OllamaChatModel;

// 统一接口
ChatLanguageModel model;

// 切换到 OpenAI
model = OpenAiChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("gpt-4o")
        .build();

// 切换到 Anthropic Claude
model = AnthropicChatModel.builder()
        .apiKey(System.getenv("ANTHROPIC_API_KEY"))
        .modelName("claude-3-sonnet-20240229")
        .build();

// 切换到 Ollama(本地模型)
model = OllamaChatModel.builder()
        .baseUrl("http://localhost:11434")
        .modelName("llama3.1")
        .build();

// 使用相同的代码调用
String response = model.generate("你好");

3.2 配置差异对比

配置项 OpenAI Anthropic Ollama
API Key ✅ 必需 ✅ 必需 ❌ 不需要
Base URL 默认 默认 必需(http://localhost:11434
Model Name gpt-4o, gpt-3.5-turbo claude-3-sonnet, claude-3-opus llama3.1, qwen2
温度 支持 支持 支持
流式响应 支持 支持 支持
多模态 gpt-4-vision-preview claude-3-opus-20240229 依赖模型
成本 按用量计费 按用量计费 免费(本地)

3.3 配置工厂模式

java 复制代码
public class ModelFactory {
    
    public static ChatLanguageModel createModel(String provider, String modelName) {
        switch (provider.toLowerCase()) {
            case "openai":
                return OpenAiChatModel.builder()
                        .apiKey(System.getenv("OPENAI_API_KEY"))
                        .modelName(modelName)
                        .build();
            
            case "anthropic":
                return AnthropicChatModel.builder()
                        .apiKey(System.getenv("ANTHROPIC_API_KEY"))
                        .modelName(modelName)
                        .build();
            
            case "ollama":
                return OllamaChatModel.builder()
                        .baseUrl("http://localhost:11434")
                        .modelName(modelName)
                        .build();
            
            default:
                throw new IllegalArgumentException("Unsupported provider: " + provider);
        }
    }
}

// 使用配置工厂
ChatLanguageModel model = ModelFactory.createModel("openai", "gpt-4o");
String response = model.generate("你好");

3.4 模型切换流程图

复制代码
┌─────────────────────────────────────────────────────────────┐
│                   模型提供商切换流程                          │
└─────────────────────────────────────────────────────────────┘

┌─────────────┐    ┌──────────────┐    ┌─────────────────┐
│  配置源      │ -> │ ModelFactory │ -> │ ChatLanguageModel │
│  配置文件    │    │  创建模型    │    │   统一接口      │
│  环境变量    │    │              │    │                 │
└─────────────┘    └──────────────┘    └────────┬────────┘
                                             │
                      ┌──────────────────────┼──────────────────────┐
                      │                      │                      │
                      ▼                      ▼                      ▼
              ┌───────────────┐    ┌───────────────┐    ┌───────────────┐
              │  OpenAI       │    │  Anthropic    │    │  Ollama       │
              │  gpt-4o       │    │  claude-3      │    │  llama3.1     │
              │  云端API      │    │  云端API      │    │  本地部署      │
              └───────────────┘    └───────────────┘    └───────────────┘
                      │                      │                      │
                      └──────────────────────┼──────────────────────┘
                                             │
                                             ▼
                                      ┌──────────────┐
                                      │ 统一调用接口  │
                                      │ generate()   │
                                      └──────────────┘
                                             │
                                             ▼
                                      ┌──────────────┐
                                      │   返回结果    │
                                      │  String      │
                                      └──────────────┘

四、通用参数标准化

4.1 温度(Temperature)

控制输出的随机性,范围 0.0-2.0。

java 复制代码
OpenAiChatModel model = OpenAiChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("gpt-4o")
        .temperature(0.7)  // 默认值 0.7
        .build();

// 温度值解释
// 0.0 - 0.2:确定性输出,适合代码生成、事实问答
// 0.3 - 0.7:平衡输出,适合大多数场景
// 0.8 - 1.5:创造性输出,适合故事创作、头脑风暴
// 1.6 - 2.0:高度随机,适合生成多样内容

4.2 Token 限制(Max Tokens)

控制输出的最大长度。

java 复制代码
OpenAiChatModel model = OpenAiChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("gpt-4o")
        .maxTokens(1000)  // 默认值因模型而异
        .build();

// Token 计算参考
// 1 Token ≈ 4 英文字符 或 2 个汉字
// 1000 Tokens ≈ 750 英文单词 或 500 汉字

4.3 超时(Timeout)

控制请求的超时时间。

java 复制代码
OpenAiChatModel model = OpenAiChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("gpt-4o")
        .timeout(Duration.ofSeconds(60))  // 默认 60 秒
        .build();

// 超时处理策略
try {
    String response = model.generate("长文本生成任务...");
} catch (TimeoutException e) {
    System.err.println("请求超时,请重试或调整超时时间");
}

4.4 停止序列(Stop Sequences)

定义输出停止的标记。

java 复制代码
OpenAiChatModel model = OpenAiChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("gpt-4o")
        .stop_sequences("\n\n", "###")  // 遇到这些标记停止生成
        .build();

4.5 Top-P 与 Top-K

java 复制代码
OpenAiChatModel model = OpenAiChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("gpt-4o")
        .topP(0.9)    // 核采样,范围 0.0-1.0,默认 1.0
        .topK(40)     // 选择前 K 个词,仅部分模型支持
        .build();

五、实战示例:多模型问答系统

5.1 定义统一接口

java 复制代码
import dev.langchain4j.service.AiService;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;

@AiService
public interface QuestionAssistant {
    
    @SystemMessage("你是一个知识渊博的助手,用简洁的语言回答问题")
    String answer(@UserMessage String question);
}

5.2 构建多模型实例

java 复制代码
public class MultiModelSystem {
    
    private final Map<String, QuestionAssistant> assistants = new HashMap<>();
    
    public void init() {
        // OpenAI 实例
        ChatLanguageModel openaiModel = OpenAiChatModel.builder()
                .apiKey(System.getenv("OPENAI_API_KEY"))
                .modelName("gpt-4o")
                .build();
        
        QuestionAssistant openaiAssistant = AiServices.builder(QuestionAssistant.class)
                .chatLanguageModel(openaiModel)
                .build();
        
        assistants.put("openai", openaiAssistant);
        
        // Ollama 实例
        ChatLanguageModel ollamaModel = OllamaChatModel.builder()
                .baseUrl("http://localhost:11434")
                .modelName("llama3.1")
                .build();
        
        QuestionAssistant ollamaAssistant = AiServices.builder(QuestionAssistant.class)
                .chatLanguageModel(ollamaModel)
                .build();
        
        assistants.put("ollama", ollamaAssistant);
    }
    
    public String answer(String provider, String question) {
        QuestionAssistant assistant = assistants.get(provider);
        if (assistant == null) {
            throw new IllegalArgumentException("Unknown provider: " + provider);
        }
        return assistant.answer(question);
    }
}

5.3 使用示例

java 复制代码
public class Main {
    public static void main(String[] args) {
        MultiModelSystem system = new MultiModelSystem();
        system.init();
        
        String question = "什么是 LangChain4j?";
        
        // 使用 OpenAI 回答
        String openaiAnswer = system.answer("openai", question);
        System.out.println("OpenAI: " + openaiAnswer);
        
        // 使用 Ollama 回答
        String ollamaAnswer = system.answer("ollama", question);
        System.out.println("Ollama: " + ollamaAnswer);
    }
}

六、常见问题

Q1: 如何处理不同模型的 Token 限制差异?

A: 使用 maxTokens 参数或自动调整:

java 复制代码
int maxTokens = switch (provider) {
    case "openai" -> 4096;
    case "anthropic" -> 4096;
    case "ollama" -> 2048;  // 本地模型通常限制较小
    default -> 1024;
};

Q2: 多模态输入支持哪些模型?

A: 当前支持以下视觉模型:

  • OpenAI: gpt-4-vision-preview, gpt-4o
  • Anthropic: claude-3-opus-20240229, claude-3-sonnet-20240229
  • Google AI: gemini-pro-vision

Q3: 如何迁移现有代码到新模型?

A: 只需修改模型创建代码,业务逻辑无需改动:

java 复制代码
// 旧代码
ChatLanguageModel model = OpenAiChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .build();

// 新代码:切换到 Anthropic
ChatLanguageModel model = AnthropicChatModel.builder()
        .apiKey(System.getenv("ANTHROPIC_API_KEY"))
        .build();

// 业务逻辑无需改动
String response = model.generate("你好");

七、小结

本文深入剖析了 LangChain4j 的核心抽象,包括:

  1. 消息类型体系:SystemMessage、UserMessage、AiMessage、ToolExecutionResultMessage
  2. Content 抽象:TextContent、ImageContent 支持多模态输入
  3. 模型提供商切换:统一接口设计,支持无缝切换 OpenAI、Anthropic、Ollama
  4. 通用参数标准化:温度、Token 限制、超时、停止序列等
  5. 实战示例:多模型问答系统

核心思想: 通过统一的抽象层,实现"模型无关"的设计,让开发者能够灵活切换 LLM 提供商,而无需重写业务代码。

下一步学习:

  • 文章 3:《LangChain4j 异常处理与测试框架:企业级健壮性实践》

参考文献:

相关推荐
LJ97951112 小时前
当AI遇上媒体发布:企业传播的下一站
大数据·人工智能
智算菩萨2 小时前
基于多模态基础模型迈向通用人工智能:BriVL模型深度解析
论文阅读·人工智能·ai·语言模型·agi
小鹿软件办公2 小时前
OpenAI 补齐产品线:GPT-5.4 Mini 与 Nano 正式发布
人工智能·openai
qq_233772712 小时前
元——人工智能
人工智能
大傻^2 小时前
SpringAI 2.0 可观测性体系:AI 操作追踪、指标监控与评估框架
人工智能·springai·指标监控·评估框架
GIS数据转换器2 小时前
小龙虾(OpenClaw) 在低空经济领域的应用
大数据·人工智能·无人机·智慧城市·制造
用户69371750013842 小时前
OS级AI Agent:手机操作系统的下一个战场
android·前端·人工智能
大胖某人2 小时前
Kali系统安装OpenClaw调用DeepSeek API部署方法详解
linux·人工智能
BJ_Bonree2 小时前
直播预告 | 三步构建可观测体系,守护制造业业务连续性
人工智能·可观测性