消息元数据
ChatClient 支持向用户和系统消息添加元数据。元数据提供了有关消息的额外上下文和信息,可由 AI 模型或下游处理使用。
向用户消息添加元数据
您可以使用 metadata() 方法向用户消息添加元数据。
// Adding individual metadata key-value pairs
String response = chatClient.prompt()
.user(u -> u.text("What's the weather like?")
.metadata("messageId", "msg-123")
.metadata("userId", "user-456")
.metadata("priority", "high"))
.call()
.content();
// Adding multiple metadata entries at once
Map<String, Object> userMetadata = Map.of(
"messageId", "msg-123",
"userId", "user-456",
"timestamp", System.currentTimeMillis()
);
String response = chatClient.prompt()
.user(u -> u.text("What's the weather like?")
.metadata(userMetadata))
.call()
.content();
向系统消息添加元数据
同样,您可以向系统消息添加元数据。
// Adding metadata to system messages
String response = chatClient.prompt()
.system(s -> s.text("You are a helpful assistant.")
.metadata("version", "1.0")
.metadata("model", "gpt-4"))
.user("Tell me a joke")
.call()
.content();
默认元数据支持
您还可以在 ChatClient 构建器级别配置默认元数据。
@Configuration
class Config {
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem(s -> s.text("You are a helpful assistant")
.metadata("assistantType", "general")
.metadata("version", "1.0"))
.defaultUser(u -> u.text("Default user context")
.metadata("sessionId", "default-session"))
.build();
}
}
元数据验证
ChatClient 验证元数据以确保数据完整性。
- 元数据键不能为 null 或空。
- 元数据值不能为 null。
- 传递 Map 时,键和值都不能包含 null 元素。
元数据验证失败,抛出异常:IllegalArgumentException
访问元数据
元数据包含在生成的 UserMessage 和 SystemMessage 对象中,可以通过消息的 getMetadata() 方法访问。这在 Advisor 中处理消息或检查对话历史记录时特别有用。
示例:MessageMetadataAdvisor
package com.yupi.yuaiagent.advisor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisor;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisorChain;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import reactor.core.publisher.Flux;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
/**
* 演示在 Advisor 中读取 ChatClient 为 User/System 消息设置的元数据(Message metadata),
* 并同步到本次调用的 context,便于链路内其它组件使用。
*/
@Slf4j
public class MessageMetadataAdvisor implements CallAdvisor, StreamAdvisor {
/** 放入 context 的 key:当前请求用户消息元数据(只读快照) */
public static final String CTX_USER_MESSAGE_METADATA = "userMessageMetadata";
/** 放入 context 的 key:当前请求系统消息元数据(只读快照) */
public static final String CTX_SYSTEM_MESSAGE_METADATA = "systemMessageMetadata";
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
return chain.nextCall(enrichRequestWithMessageMetadata(request));
}
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest request, StreamAdvisorChain chain) {
return chain.nextStream(enrichRequestWithMessageMetadata(request));
}
/**
* 从 Prompt 中的 UserMessage / SystemMessage 读取 getMetadata(),写入 request.context()。
*/
private ChatClientRequest enrichRequestWithMessageMetadata(ChatClientRequest request) {
Prompt prompt = request.prompt();
UserMessage userMessage = prompt.getUserMessage();
Map<String, Object> userMeta = userMessage == null
? Collections.emptyMap()
: Optional.ofNullable(userMessage.getMetadata()).orElseGet(Collections::emptyMap);
SystemMessage systemMessage = prompt.getSystemMessage();
Map<String, Object> systemMeta = systemMessage == null
? Collections.emptyMap()
: Optional.ofNullable(systemMessage.getMetadata()).orElseGet(Collections::emptyMap);
if (!userMeta.isEmpty()) {
log.info("[{}] user message metadata: {}", getName(), userMeta);
request.context().put(CTX_USER_MESSAGE_METADATA, Map.copyOf(userMeta));
// 示例:根据业务元数据影响下游行为(其它 Advisor 可读 context)
Object priority = userMeta.get("priority");
if ("high".equals(priority)) {
request.context().put("priorityHigh", Boolean.TRUE);
}
}
if (!systemMeta.isEmpty()) {
log.info("[{}] system message metadata: {}", getName(), systemMeta);
request.context().put(CTX_SYSTEM_MESSAGE_METADATA, Map.copyOf(systemMeta));
}
return request;
}
@Override
public int getOrder() {
return 0;
}
@Override
public String getName() {
return this.getClass().getSimpleName();
}
}