Spring AI(1.1.0):消息元数据

消息元数据

ChatClient 支持向用户和系统消息添加元数据。元数据提供了有关消息的额外上下文和信息,可由 AI 模型或下游处理使用。

向用户消息添加元数据

您可以使用 metadata() 方法向用户消息添加元数据。

less 复制代码
// 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();

向系统消息添加元数据

同样,您可以向系统消息添加元数据。

less 复制代码
// 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 构建器级别配置默认元数据。

less 复制代码
@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

typescript 复制代码
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();
    }
}
相关推荐
武子康10 分钟前
Cosmos Curator 总览:从一条样本的组成,选对数据处理入口
人工智能·后端·agent
北冥you鱼1 小时前
Go 语言 CRUD 实战:从接口设计到真实业务场景
开发语言·后端·golang
Lichee201 小时前
招行Java 面试知识点梳理:Spring 事务、锁、线程池、集合
java·面试
锋行天下1 小时前
Langgraph 实现类似Langchain记忆上下文过长裁剪
人工智能·后端
YDS8291 小时前
Small Spring IOC篇:作用域和FactoryBean
java·后端·spring
Shaoshing1 小时前
LeetCode100 42.接雨水
java·算法·leetcode
枫子有风1 小时前
卡码网盘项目深挖
java·redis·jwt·es·pgvector
郭小李1 小时前
springmvc 拦截器
java·开发语言·前端
大风起兮云飞扬丶1 小时前
Java——日期类
java·开发语言
Wang's Blog2 小时前
Java 项目实战:交友项目 - 联系人管理需求分析
java·开发语言·交友