AI Services:重写 ChatRequest

在与大语言模型(LLM)交互的过程中,ChatRequest 作为承载对话上下文和模型配置的核心数据结构,在发送给模型前对其进行重写,是实现灵活业务适配的关键能力。

因此,在某些情况下,在将 ChatRequest 发送到 LLM(大语言模型) 之前对其进行修改会非常有用。例如,可能需要在用户消息后附加一些额外的上下文,或者根据某些外部条件修改系统消息。

UnaryOperator<ChatRequest>

上面这些需求,均可以通过创建一个 UnaryOperator<ChatRequest>,然后通过 chatRequestTransformer() 方法配置给 AI 服务来实现这一点。

方法定义如下:

public AiServices<T> chatRequestTransformer(UnaryOperator<ChatRequest> chatRequestTransformer) {
    this.context.chatRequestTransformer = (req, memId) -> {
        return (ChatRequest)chatRequestTransformer.apply(req);
    };
    return this;
}

UnaryOperator 运算符实现了要应用于 ChatRequest 的转换,可以这样做:

Assistant assistant = AiServices.builder(Assistant.class)
    .chatModel(model)
    .chatRequestTransformer(transformingFunction)  // 配置要应用于 ChatRequest 的转换函数
    .build();

什么是 UnaryOperator?

UnaryOperator 是 Java 8 引入的一个函数式接口(位于 java.util.function 包下),它属于 Function 接口的特殊子类。

它的核心特点是:输入参数和返回值的类型完全相同,相当于「一元操作符」—— 接收一个同类型参数,经过处理后返回一个同类型结果,没有其他额外输出。

我们可以在 UnaryOperator 的 apply() 方法中实现对 ChatRequest 的修改。

简单示例

下面示例通过 UnaryOperator 在 ChatRequest 中添加一个系统消息 SystemMessage,以及修改用户消息 UserMessage,添加输出限制。

代码如下:

package com.hxstrive.langchain4j.aiServices;
import dev.langchain4j.data.message.*;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiChatRequestParameters;
import dev.langchain4j.service.AiServices;
import java.util.ArrayList;
import java.util.List;
public class RewriteChatRequestDemo {
    // 推荐:将OPEN_API_KEY设置成环境变量, 避免硬编码或随着代码泄露
    // 注意,设置完环境变量记得重启IDEA,不然可能环境变量不会生效
    private static final String API_KEY = System.getenv("OPEN_API_KEY");
    
    // 定义业务接口
    interface Assistant {
        // userMessage 是用户输入的消息
        // 返回值是 AI 回复的内容呢
        String chat(String userMessage);
    }
    
    public static void main(String[] args) {
        // 创建 ChatModel 实现类(OpenAI 为例)
        ChatModel chatModel = OpenAiChatModel.builder()
                .baseUrl("https://api.xty.app/v1")
                .apiKey(API_KEY)
                .modelName("gpt-3.5-turbo")
                .temperature(0.7)
                .logRequests(true)
                .logResponses(true)
                .build();
        
        // 使用 AiServices 创建服务
        Assistant assistant = AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .chatRequestTransformer((originalRequest) -> {
                    // 步骤1:获取原始请求中的所有消息
                    final List<ChatMessage> originalMessages = originalRequest.messages();
                    final List<ChatMessage> modifiedMessages = new ArrayList<>(originalMessages);
                    
                    // 步骤2:重写/修改请求内容(示例场景)
                    // 给所有请求添加一个统一的系统提示(前置追加)
                    SystemMessage customSystemMessage = SystemMessage.from(
                            "你是一个严谨的助手,名字叫做“小彩”。"
                    );
                    modifiedMessages.add(0, customSystemMessage); // 插入到消息列表头部
                    
                    // 修改用户消息的内容(例如补充格式要求)
                    for (int i = 0; i < modifiedMessages.size(); i++) {
                        ChatMessage message = modifiedMessages.get(i);
                        // 可以使用 message.type() == ChatMessageType.USER 进行判断,
                        // 但是,笔者 IDEA 识别不了 ChatMessageType 和 ContentType,不知什么原因
                        if (message instanceof UserMessage) {
                            UserMessage originalUserMsg = (UserMessage) message;
                            List<Content> contents = new ArrayList<>(originalUserMsg.contents());
                            for (int j = 0; j < contents.size(); j++) {
                                if(contents.get(j) instanceof TextContent) {
                                    TextContent textContent = (TextContent) contents.get(j);
                                    String newContent = textContent.text() + "(注意:请使用英文和中文回复)";
                                    modifiedMessages.set(i, UserMessage.from(newContent));
                                }
                            }
                            break; // 只修改第一个用户消息,按需调整
                        }
                    }
                    
                    // 步骤3:构建并返回修改后的 ChatRequest
                    return originalRequest.toBuilder()
                            .messages(modifiedMessages) // 替换为修改后的消息列表
                            .parameters(OpenAiChatRequestParameters.builder()
                                    .temperature(0.3) // 修改参数
                                    .maxOutputTokens(1024) // 修改参数
                                    .build())
                            .build();
                })
                .build();
                
        // 发起对话
        String answer = assistant.chat("你好! 你叫什么名字?");
        System.out.println(answer);
    }
}

运行示例,输出日志如下:

// 请求报文
14:07:15.893 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
  "model" : "gpt-3.5-turbo",
  "messages" : [ {
    "role" : "system",
    "content" : "你是一个严谨的助手,名字叫做“小彩”。"
  }, {
    "role" : "user",
    "content" : "你好! 你叫什么名字?(注意:请使用英文和中文回复)"
  } ],
  "temperature" : 0.3,
  "stream" : false,
  "max_tokens" : 1024
}

// 响应报文
14:07:22.448 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP response:
- status code: 200
- headers: [:status: 200], [access-control-allow-headers: *], [access-control-allow-methods: POST, GET, OPTIONS, DELETE,PUT], [access-control-allow-origin: *], [access-control-max-age: 3600], [alt-svc: h3=":443"; ma=86400], [cf-cache-status: DYNAMIC], [cf-ray: 9c7fbf431a3cf5e2-AMS], [content-length: 734], [content-type: application/json;charset=UTF-8], [date: Tue, 03 Feb 2026 06:07:22 GMT], [nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}], [report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=hNxz8MwvpJ%2FG%2Fge1DttU23jrSpOL78tiQr7OjBTqZpAfdEuNqbfMCiEl5Gj3KgGbbasqcqrAJzlZjSh%2FSGzKVjV6CxuBLkyoaA%3D%3D"}]}], [server: cloudflare], [server-timing: [cfCacheStatus;desc="DYNAMIC", cfEdge;dur=9,cfOrigin;dur=5283]], [x-oneapi-request-id: 20260203140717207806817lAGVMyNz]
- body: {
    "id": "chatcmpl-T6U7Q7KHjurp8vngqLsCjeoINPZA1",
    "object": "chat.completion",
    "created": 1770098840,
    "model": "gpt-3.5-turbo-0613",
    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": "你好!很高兴认识你 😊  \nHello! Nice to meet you!\n\n**中文:** 我叫 **小彩**。  \n**English:** My name is **Xiaocai**.\n\n随时找我聊天或帮忙都可以~  \nFeel free to chat with me anytime!"
            },
            "finish_reason": "stop"
        }
    ],
    "usage": {
        "prompt_tokens": 60,
        "completion_tokens": 76,
        "total_tokens": 136
    },
    "system_fingerprint": "fp_b28b39ffa8"
}
你好!很高兴认识你 😊  
Hello! Nice to meet you!
**中文:** 我叫 **小彩**。  
**English:** My name is **Xiaocai**.
随时找我聊天或帮忙都可以~  
Feel free to chat with me anytime!
Process finished with exit code 0

BiFunction<ChatRequest, Object, ChatRequest>

如果还需要访问聊天记忆(ChatMemory)来实现所需的聊天请求(ChatRequest)转换,也可以使用 BiFunction<ChatRequest, Object, ChatRequest> 来配置 chatRequestTransformer() 方法,该函数的第二个参数为记忆 ID。

方法定义如下:

public AiServices<T> chatRequestTransformer(BiFunction<ChatRequest, Object, ChatRequest> chatRequestTransformer) {
    this.context.chatRequestTransformer = chatRequestTransformer;
    return this;
}

使用方式如下:

AiServices.builder(SimpleAiServicesDemo.Assistant.class)
        .chatModel(chatModel)
        .chatRequestTransformer((originalRequest, memoryId) -> {
            //...这里写修改逻辑...
            return originalRequest;
        })
        .build();

简单示例

package com.hxstrive.langchain4j.aiServices;

import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import java.util.ArrayList;
import java.util.List;

public class RewriteChatRequestDemo2 {
    // 推荐:将OPEN_API_KEY设置成环境变量, 避免硬编码或随着代码泄露
    // 注意,设置完环境变量记得重启IDEA,不然可能环境变量不会生效
    private static final String API_KEY = System.getenv("OPEN_API_KEY");
    
    // 定义业务接口
    interface Assistant {
        // userMessage 是用户输入的消息
        // 返回值是 AI 回复的内容呢
        @UserMessage("{{userMessage}}")
        String chat(@MemoryId String memoryId, @V("userMessage") String userMessage);
    }
    
    public static void main(String[] args) {
        // 创建 ChatModel 实现类(OpenAI 为例)
        ChatModel chatModel = OpenAiChatModel.builder()
                .baseUrl("https://api.xty.app/v1")
                .apiKey(API_KEY)
                .modelName("gpt-3.5-turbo")
                .temperature(0.7)
                .logRequests(true)
                .logResponses(true)
                .build();
        
        final String myMemoryId = "45de4db8-4ed87ef2d44d9fd44dd8b8de";
        ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
        
        // 使用 AiServices 创建服务
        Assistant assistant = AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                // 设置默认的聊天记忆 ChatMemory
                .chatMemory(chatMemory)
                // 为指定的聊天记忆ID返回 ChatMemory
                // 如果聊天记忆ID是特意设计过的,是不是可以通过聊天记忆ID选择不同的 ChatMemory
                // 如以 TEMP- 开头的使用内存存储,REDIS- 开头的使用Redis存储,DB- 开头的使用数据库存储
                .chatMemoryProvider((chatMemoryId) -> chatMemory)
                .chatRequestTransformer((originalRequest, memoryId) -> {
                    // 步骤1:非空校验,保障健壮性
                    if (memoryId == null || originalRequest == null) {
                        return originalRequest;
                    }
                    System.out.println("originalRequest: " + originalRequest);
                    System.out.println("memoryId: " + memoryId);
                    System.out.println("\n===========================================\n");
                    // 步骤2:整理消息列表,追加系统提示(要求结合历史记录回答)
                    List<ChatMessage> modifiedMessages = new ArrayList<>(originalRequest.messages());
                    SystemMessage customSystemMessage = SystemMessage.from(
                            "你需要结合用户的历史聊天记录回答问题,回答简洁明了,不超过50字。"
                    );
                    modifiedMessages.add(0, customSystemMessage);
                    // 步骤3:构建修改后的 ChatRequest
                    return originalRequest.toBuilder()
                            .messages(modifiedMessages)
                            .build();
                })
                .build();
                
        // 发起对话
        String answer = assistant.chat(myMemoryId, "你好! 我的名字是张三。");
        System.out.println(answer);
        String answerWithName = assistant.chat(myMemoryId, "我叫什么名字?");
        System.out.println(answerWithName);
    }
}

运行示例,输出日志如下:

originalRequest: ChatRequest { messages = [UserMessage { name = null, contents = [TextContent { text = "你好! 我的名字是张三。" }], attributes = {} }], parameters = DefaultChatRequestParameters{modelName='null', temperature=null, topP=null, topK=null, frequencyPenalty=null, presencePenalty=null, maxOutputTokens=null, stopSequences=[], toolSpecifications=[], toolChoice=null, responseFormat=null} }
memoryId: 45de4db8-4ed87ef2d44d9fd44dd8b8de
===========================================
14:53:04.315 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
  "model" : "gpt-3.5-turbo",
  "messages" : [ {
    "role" : "system",
    "content" : "你需要结合用户的历史聊天记录回答问题,回答简洁明了,不超过50字。"
  }, {
    "role" : "user",
    "content" : "你好! 我的名字是张三。"
  } ],
  "temperature" : 0.7,
  "stream" : false
}

14:53:11.234 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP response:
- status code: 200
- headers: [:status: 200], [access-control-allow-headers: *], [access-control-allow-methods: POST, GET, OPTIONS, DELETE,PUT], [access-control-allow-origin: *], [access-control-max-age: 3600], [alt-svc: h3=":443"; ma=86400], [cf-cache-status: DYNAMIC], [cf-ray: 9c800266bf47ef92-AMS], [content-length: 555], [content-type: application/json;charset=UTF-8], [date: Tue, 03 Feb 2026 06:53:10 GMT], [nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}], [report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=sk%2Fhph7IveJmtOvYfFAUgHB546oqjvg%2BSRkNCzgHL5C8IU%2B35kgBPDHZvrodMKWfo24HHjc8bGa06vIrjv2aGe5OHeYROZPOCCDv"}]}], [server: cloudflare], [server-timing: [cfCacheStatus;desc="DYNAMIC", cfEdge;dur=7,cfOrigin;dur=3852]], [x-oneapi-request-id: 20260203145307246422021jcpNSSEV]
- body: {
    "id": "chatcmpl-zOr5gM9JtpWoj5RwCo4K5GAR2RzYy",
    "object": "chat.completion",
    "created": 1770101589,
    "model": "gpt-3.5-turbo-0613",
    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": "你好,张三!很高兴认识你 😊"
            },
            "finish_reason": "stop"
        }
    ],
    "usage": {
        "prompt_tokens": 56,
        "completion_tokens": 17,
        "total_tokens": 73
    },
    "system_fingerprint": "fp_b28b39ffa8"
}
你好,张三!很高兴认识你 😊

originalRequest: ChatRequest { messages = [UserMessage { name = null, contents = [TextContent { text = "你好! 我的名字是张三。" }], attributes = {} }, AiMessage { text = "你好,张三!很高兴认识你 😊", thinking = null, toolExecutionRequests = [], attributes = {} }, UserMessage { name = null, contents = [TextContent { text = "我叫什么名字?" }], attributes = {} }], parameters = DefaultChatRequestParameters{modelName='null', temperature=null, topP=null, topK=null, frequencyPenalty=null, presencePenalty=null, maxOutputTokens=null, stopSequences=[], toolSpecifications=[], toolChoice=null, responseFormat=null} }
memoryId: 45de4db8-4ed87ef2d44d9fd44dd8b8de
===========================================

14:53:11.288 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
  "model" : "gpt-3.5-turbo",
  "messages" : [ {
    "role" : "system",
    "content" : "你需要结合用户的历史聊天记录回答问题,回答简洁明了,不超过50字。"
  }, {
    "role" : "user",
    "content" : "你好! 我的名字是张三。"
  }, {
    "role" : "assistant",
    "content" : "你好,张三!很高兴认识你 😊"
  }, {
    "role" : "user",
    "content" : "我叫什么名字?"
  } ],
  "temperature" : 0.7,
  "stream" : false
}

14:53:15.333 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP response:
- status code: 200
- headers: [:status: 200], [access-control-allow-headers: *], [access-control-allow-methods: POST, GET, OPTIONS, DELETE,PUT], [access-control-allow-origin: *], [access-control-max-age: 3600], [alt-svc: h3=":443"; ma=86400], [cf-cache-status: DYNAMIC], [cf-ray: 9c80028299aaef92-AMS], [content-length: 528], [content-type: application/json;charset=UTF-8], [date: Tue, 03 Feb 2026 06:53:14 GMT], [nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}], [report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=GaYJ1w1r06XOopjn61bPSbBQM55CDDxrEMMUjZv6OjaPNcb%2FWdTG7sbYf8T0ZDFMe%2Fue18YuSnlxbe%2BAh%2FrHtVyTmbT%2BAANuazh4"}]}], [server: cloudflare], [server-timing: [cfCacheStatus;desc="DYNAMIC", cfEdge;dur=2,cfOrigin;dur=3468]], [x-oneapi-request-id: 20260203145311537327136OkNWVRe7]
- body: {
    "id": "chatcmpl-q2RGh2XEDdxzsgcayt8xo6KQWu7K1",
    "object": "chat.completion",
    "created": 1770101594,
    "model": "gpt-3.5-turbo-0613",
    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": "你叫张三。"
            },
            "finish_reason": "stop"
        }
    ],
    "usage": {
        "prompt_tokens": 90,
        "completion_tokens": 6,
        "total_tokens": 96
    },
    "system_fingerprint": "fp_b28b39ffa8"
}

你叫张三。

Process finished with exit code 0

更多 LangChain4j 知识请阅读后续教程……

   

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号