前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >聊聊Spring AI的Advisors

聊聊Spring AI的Advisors

原创
作者头像
code4it
发布于 2025-03-30 05:48:14
发布于 2025-03-30 05:48:14
8600
代码可运行
举报
文章被收录于专栏:码匠的流水账码匠的流水账
运行总次数:0
代码可运行

本文主要研究一下Spring AI的Advisors

Advisor

org/springframework/ai/chat/client/advisor/api/Advisor.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Advisor extends Ordered {

  /**
   * Useful constant for the default Chat Memory precedence order. Ensures this order
   * has lower priority (e.g. precedences) than the Spring AI internal advisors. It
   * leaves room (1000 slots) for the user to plug in their own advisors with higher
   * priority.
   */
  int DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER = Ordered.HIGHEST_PRECEDENCE + 1000;

  /**
   * Return the name of the advisor.
   * @return the advisor name.
   */
  String getName();

}

Advisor接口继承了Ordered,定义了getName方法

CallAroundAdvisor

org/springframework/ai/chat/client/advisor/api/CallAroundAdvisor.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface CallAroundAdvisor extends Advisor {

  /**
   * Around advice that wraps the ChatModel#call(Prompt) method.
   * @param advisedRequest the advised request
   * @param chain the advisor chain
   * @return the response
   */
  AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);

}

CallAroundAdvisor继承了Advisor接口,它定义了aroundCall方法,入参是AdvisedRequest及CallAroundAdvisorChain,返回AdvisedResponse

StreamAroundAdvisor

org/springframework/ai/chat/client/advisor/api/StreamAroundAdvisor.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface StreamAroundAdvisor extends Advisor {

  /**
   * Around advice that wraps the invocation of the advised request.
   * @param advisedRequest the advised request
   * @param chain the chain of advisors to execute
   * @return the result of the advised request
   */
  Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);

}

StreamAroundAdvisor继承了Advisor接口,它定义了aroundStream方法,入参是AdvisedRequest及StreamAroundAdvisorChain,返回的是Flux

SimpleLoggerAdvisor

org/springframework/ai/chat/client/advisor/SimpleLoggerAdvisor.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

  public static final Function<AdvisedRequest, String> DEFAULT_REQUEST_TO_STRING = request -> request.toString();

  public static final Function<ChatResponse, String> DEFAULT_RESPONSE_TO_STRING = response -> ModelOptionsUtils
    .toJsonString(response);

  private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);

  private final Function<AdvisedRequest, String> requestToString;

  private final Function<ChatResponse, String> responseToString;

  private int order;

  public SimpleLoggerAdvisor() {
    this(DEFAULT_REQUEST_TO_STRING, DEFAULT_RESPONSE_TO_STRING, 0);
  }

  public SimpleLoggerAdvisor(int order) {
    this(DEFAULT_REQUEST_TO_STRING, DEFAULT_RESPONSE_TO_STRING, order);
  }

  public SimpleLoggerAdvisor(Function<AdvisedRequest, String> requestToString,
      Function<ChatResponse, String> responseToString, int order) {
    this.requestToString = requestToString;
    this.responseToString = responseToString;
    this.order = order;
  }

  @Override
  public String getName() {
    return this.getClass().getSimpleName();
  }

  @Override
  public int getOrder() {
    return this.order;
  }

  private AdvisedRequest before(AdvisedRequest request) {
    logger.debug("request: {}", this.requestToString.apply(request));
    return request;
  }

  private void observeAfter(AdvisedResponse advisedResponse) {
    logger.debug("response: {}", this.responseToString.apply(advisedResponse.response()));
  }

  @Override
  public String toString() {
    return SimpleLoggerAdvisor.class.getSimpleName();
  }

  @Override
  public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

    advisedRequest = before(advisedRequest);

    AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);

    observeAfter(advisedResponse);

    return advisedResponse;
  }

  @Override
  public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

    advisedRequest = before(advisedRequest);

    Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);

    return new MessageAggregator().aggregateAdvisedResponse(advisedResponses, this::observeAfter);
  }

}

SimpleLoggerAdvisor实现了CallAroundAdvisor、StreamAroundAdvisor接口,内部定义了before、observeAfter方法打印request和response,其aroundCall、aroundStream分别在chain执行next之前调用before,之后调用observeAfter

SafeGuardAdvisor

org/springframework/ai/chat/client/advisor/SafeGuardAdvisor.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SafeGuardAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

  private final static String DEFAULT_FAILURE_RESPONSE = "I'm unable to respond to that due to sensitive content. Could we rephrase or discuss something else?";

  private final static int DEFAULT_ORDER = 0;

  private final String failureResponse;

  private final List<String> sensitiveWords;

  private final int order;

  public SafeGuardAdvisor(List<String> sensitiveWords) {
    this(sensitiveWords, DEFAULT_FAILURE_RESPONSE, DEFAULT_ORDER);
  }

  public SafeGuardAdvisor(List<String> sensitiveWords, String failureResponse, int order) {
    Assert.notNull(sensitiveWords, "Sensitive words must not be null!");
    Assert.notNull(failureResponse, "Failure response must not be null!");
    this.sensitiveWords = sensitiveWords;
    this.failureResponse = failureResponse;
    this.order = order;
  }

  public static Builder builder() {
    return new Builder();
  }

  public String getName() {
    return this.getClass().getSimpleName();
  }

  @Override
  public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

    if (!CollectionUtils.isEmpty(this.sensitiveWords)
        && this.sensitiveWords.stream().anyMatch(w -> advisedRequest.userText().contains(w))) {

      return createFailureResponse(advisedRequest);
    }

    return chain.nextAroundCall(advisedRequest);
  }

  @Override
  public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

    if (!CollectionUtils.isEmpty(this.sensitiveWords)
        && this.sensitiveWords.stream().anyMatch(w -> advisedRequest.userText().contains(w))) {
      return Flux.just(createFailureResponse(advisedRequest));
    }

    return chain.nextAroundStream(advisedRequest);
  }

  private AdvisedResponse createFailureResponse(AdvisedRequest advisedRequest) {
    return new AdvisedResponse(ChatResponse.builder()
      .withGenerations(List.of(new Generation(new AssistantMessage(this.failureResponse))))
      .build(), advisedRequest.adviseContext());
  }

  @Override
  public int getOrder() {
    return this.order;
  }

  //......

}  

SafeGuardAdvisor实现了CallAroundAdvisor、StreamAroundAdvisor接口,其构造器可以输入sensitiveWords、failureResponse、order,其aroundCall及aroundStream方法主要是执行before逻辑,通过判断用户的输入是否包含sensitiveWords实现安全拦截,命中的话返回failureResponse,不继续往下执行。

QuestionAnswerAdvisor

org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisor.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class QuestionAnswerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

  public static final String RETRIEVED_DOCUMENTS = "qa_retrieved_documents";

  public static final String FILTER_EXPRESSION = "qa_filter_expression";

  private static final String DEFAULT_USER_TEXT_ADVISE = """

      Context information is below, surrounded by ---------------------

      ---------------------
      {question_answer_context}
      ---------------------

      Given the context and provided history information and not prior knowledge,
      reply to the user comment. If the answer is not in the context, inform
      the user that you can't answer the question.
      """;

  private static final int DEFAULT_ORDER = 0;

  private final VectorStore vectorStore;

  private final String userTextAdvise;

  private final SearchRequest searchRequest;

  private final boolean protectFromBlocking;

  private final int order;

  /**
   * The QuestionAnswerAdvisor retrieves context information from a Vector Store and
   * combines it with the user's text.
   * @param vectorStore The vector store to use
   */
  public QuestionAnswerAdvisor(VectorStore vectorStore) {
    this(vectorStore, SearchRequest.builder().build(), DEFAULT_USER_TEXT_ADVISE);
  }

  /**
   * The QuestionAnswerAdvisor retrieves context information from a Vector Store and
   * combines it with the user's text.
   * @param vectorStore The vector store to use
   * @param searchRequest The search request defined using the portable filter
   * expression syntax
   */
  public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest) {
    this(vectorStore, searchRequest, DEFAULT_USER_TEXT_ADVISE);
  }

  /**
   * The QuestionAnswerAdvisor retrieves context information from a Vector Store and
   * combines it with the user's text.
   * @param vectorStore The vector store to use
   * @param searchRequest The search request defined using the portable filter
   * expression syntax
   * @param userTextAdvise The user text to append to the existing user prompt. The text
   * should contain a placeholder named "question_answer_context".
   */
  public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest, String userTextAdvise) {
    this(vectorStore, searchRequest, userTextAdvise, true);
  }

  /**
   * The QuestionAnswerAdvisor retrieves context information from a Vector Store and
   * combines it with the user's text.
   * @param vectorStore The vector store to use
   * @param searchRequest The search request defined using the portable filter
   * expression syntax
   * @param userTextAdvise The user text to append to the existing user prompt. The text
   * should contain a placeholder named "question_answer_context".
   * @param protectFromBlocking If true the advisor will protect the execution from
   * blocking threads. If false the advisor will not protect the execution from blocking
   * threads. This is useful when the advisor is used in a non-blocking environment. It
   * is true by default.
   */
  public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest, String userTextAdvise,
      boolean protectFromBlocking) {
    this(vectorStore, searchRequest, userTextAdvise, protectFromBlocking, DEFAULT_ORDER);
  }

  /**
   * The QuestionAnswerAdvisor retrieves context information from a Vector Store and
   * combines it with the user's text.
   * @param vectorStore The vector store to use
   * @param searchRequest The search request defined using the portable filter
   * expression syntax
   * @param userTextAdvise The user text to append to the existing user prompt. The text
   * should contain a placeholder named "question_answer_context".
   * @param protectFromBlocking If true the advisor will protect the execution from
   * blocking threads. If false the advisor will not protect the execution from blocking
   * threads. This is useful when the advisor is used in a non-blocking environment. It
   * is true by default.
   * @param order The order of the advisor.
   */
  public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchRequest, String userTextAdvise,
      boolean protectFromBlocking, int order) {

    Assert.notNull(vectorStore, "The vectorStore must not be null!");
    Assert.notNull(searchRequest, "The searchRequest must not be null!");
    Assert.hasText(userTextAdvise, "The userTextAdvise must not be empty!");

    this.vectorStore = vectorStore;
    this.searchRequest = searchRequest;
    this.userTextAdvise = userTextAdvise;
    this.protectFromBlocking = protectFromBlocking;
    this.order = order;
  }

  public static Builder builder(VectorStore vectorStore) {
    return new Builder(vectorStore);
  }

  @Override
  public String getName() {
    return this.getClass().getSimpleName();
  }

  @Override
  public int getOrder() {
    return this.order;
  }

  @Override
  public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

    AdvisedRequest advisedRequest2 = before(advisedRequest);

    AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest2);

    return after(advisedResponse);
  }

  @Override
  public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

    // This can be executed by both blocking and non-blocking Threads
    // E.g. a command line or Tomcat blocking Thread implementation
    // or by a WebFlux dispatch in a non-blocking manner.
    Flux<AdvisedResponse> advisedResponses = (this.protectFromBlocking) ?
    // @formatter:off
      Mono.just(advisedRequest)
        .publishOn(Schedulers.boundedElastic())
        .map(this::before)
        .flatMapMany(request -> chain.nextAroundStream(request))
      : chain.nextAroundStream(before(advisedRequest));
    // @formatter:on

    return advisedResponses.map(ar -> {
      if (onFinishReason().test(ar)) {
        ar = after(ar);
      }
      return ar;
    });
  }

  private AdvisedRequest before(AdvisedRequest request) {

    var context = new HashMap<>(request.adviseContext());

    // 1. Advise the system text.
    String advisedUserText = request.userText() + System.lineSeparator() + this.userTextAdvise;

    // 2. Search for similar documents in the vector store.
    String query = new PromptTemplate(request.userText(), request.userParams()).render();
    var searchRequestToUse = SearchRequest.from(this.searchRequest)
      .query(query)
      .filterExpression(doGetFilterExpression(context))
      .build();

    List<Document> documents = this.vectorStore.similaritySearch(searchRequestToUse);

    // 3. Create the context from the documents.
    context.put(RETRIEVED_DOCUMENTS, documents);

    String documentContext = documents.stream()
      .map(Document::getText)
      .collect(Collectors.joining(System.lineSeparator()));

    // 4. Advise the user parameters.
    Map<String, Object> advisedUserParams = new HashMap<>(request.userParams());
    advisedUserParams.put("question_answer_context", documentContext);

    AdvisedRequest advisedRequest = AdvisedRequest.from(request)
      .userText(advisedUserText)
      .userParams(advisedUserParams)
      .adviseContext(context)
      .build();

    return advisedRequest;
  }

  private AdvisedResponse after(AdvisedResponse advisedResponse) {
    ChatResponse.Builder chatResponseBuilder = ChatResponse.builder().from(advisedResponse.response());
    chatResponseBuilder.withMetadata(RETRIEVED_DOCUMENTS, advisedResponse.adviseContext().get(RETRIEVED_DOCUMENTS));
    return new AdvisedResponse(chatResponseBuilder.build(), advisedResponse.adviseContext());
  }

  //......

}  

QuestionAnswerAdvisor实现了CallAroundAdvisor及StreamAroundAdvisor接口,其构造器要求输入VectorStore;其before方法先执行vectorStore.similaritySearch,将结果作为question_answer_context添加到advisedUserParams,一起构建advisedRequest;其after方法将advisedResponse作为qa_retrieved_documents添加到chatResponse的metadata中

AbstractChatMemoryAdvisor

org/springframework/ai/chat/client/advisor/AbstractChatMemoryAdvisor.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class AbstractChatMemoryAdvisor<T> implements CallAroundAdvisor, StreamAroundAdvisor {

  /**
   * The key to retrieve the chat memory conversation id from the context.
   */
  public static final String CHAT_MEMORY_CONVERSATION_ID_KEY = "chat_memory_conversation_id";

  /**
   * The key to retrieve the chat memory response size from the context.
   */
  public static final String CHAT_MEMORY_RETRIEVE_SIZE_KEY = "chat_memory_response_size";

  /**
   * The default conversation id to use when no conversation id is provided.
   */
  public static final String DEFAULT_CHAT_MEMORY_CONVERSATION_ID = "default";

  /**
   * The default chat memory retrieve size to use when no retrieve size is provided.
   */
  public static final int DEFAULT_CHAT_MEMORY_RESPONSE_SIZE = 100;

  /**
   * The chat memory store.
   */
  protected final T chatMemoryStore;

  /**
   * The default conversation id.
   */
  protected final String defaultConversationId;

  /**
   * The default chat memory retrieve size.
   */
  protected final int defaultChatMemoryRetrieveSize;

  /**
   * Whether to protect from blocking.
   */
  private final boolean protectFromBlocking;

  /**
   * The order of the advisor.
   */
  private final int order;

  /**
   * Constructor to create a new {@link AbstractChatMemoryAdvisor} instance.
   * @param chatMemory the chat memory store
   */
  protected AbstractChatMemoryAdvisor(T chatMemory) {
    this(chatMemory, DEFAULT_CHAT_MEMORY_CONVERSATION_ID, DEFAULT_CHAT_MEMORY_RESPONSE_SIZE, true);
  }

  /**
   * Constructor to create a new {@link AbstractChatMemoryAdvisor} instance.
   * @param chatMemory the chat memory store
   * @param defaultConversationId the default conversation id
   * @param defaultChatMemoryRetrieveSize the default chat memory retrieve size
   * @param protectFromBlocking whether to protect from blocking
   */
  protected AbstractChatMemoryAdvisor(T chatMemory, String defaultConversationId, int defaultChatMemoryRetrieveSize,
      boolean protectFromBlocking) {
    this(chatMemory, defaultConversationId, defaultChatMemoryRetrieveSize, protectFromBlocking,
        Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER);
  }

  /**
   * Constructor to create a new {@link AbstractChatMemoryAdvisor} instance.
   * @param chatMemory the chat memory store
   * @param defaultConversationId the default conversation id
   * @param defaultChatMemoryRetrieveSize the default chat memory retrieve size
   * @param protectFromBlocking whether to protect from blocking
   * @param order the order
   */
  protected AbstractChatMemoryAdvisor(T chatMemory, String defaultConversationId, int defaultChatMemoryRetrieveSize,
      boolean protectFromBlocking, int order) {

    Assert.notNull(chatMemory, "The chatMemory must not be null!");
    Assert.hasText(defaultConversationId, "The conversationId must not be empty!");
    Assert.isTrue(defaultChatMemoryRetrieveSize > 0, "The defaultChatMemoryRetrieveSize must be greater than 0!");

    this.chatMemoryStore = chatMemory;
    this.defaultConversationId = defaultConversationId;
    this.defaultChatMemoryRetrieveSize = defaultChatMemoryRetrieveSize;
    this.protectFromBlocking = protectFromBlocking;
    this.order = order;
  }

  @Override
  public String getName() {
    return this.getClass().getSimpleName();
  }

  @Override
  public int getOrder() {
    // by default the (Ordered.HIGHEST_PRECEDENCE + 1000) value ensures this order has
    // lower priority (e.g. precedences) than the internal Spring AI advisors. It
    // leaves room (1000 slots) for the user to plug in their own advisors with higher
    // priority.
    return this.order;
  }

  /**
   * Get the chat memory store.
   * @return the chat memory store
   */
  protected T getChatMemoryStore() {
    return this.chatMemoryStore;
  }

  /**
   * Get the default conversation id.
   * @param context the context
   * @return the default conversation id
   */
  protected String doGetConversationId(Map<String, Object> context) {

    return context.containsKey(CHAT_MEMORY_CONVERSATION_ID_KEY)
        ? context.get(CHAT_MEMORY_CONVERSATION_ID_KEY).toString() : this.defaultConversationId;
  }

  /**
   * Get the default chat memory retrieve size.
   * @param context the context
   * @return the default chat memory retrieve size
   */
  protected int doGetChatMemoryRetrieveSize(Map<String, Object> context) {
    return context.containsKey(CHAT_MEMORY_RETRIEVE_SIZE_KEY)
        ? Integer.parseInt(context.get(CHAT_MEMORY_RETRIEVE_SIZE_KEY).toString())
        : this.defaultChatMemoryRetrieveSize;
  }

  /**
   * Execute the next advisor in the chain.
   * @param advisedRequest the advised request
   * @param chain the advisor chain
   * @param beforeAdvise the before advise function
   * @return the advised response
   */
  protected Flux<AdvisedResponse> doNextWithProtectFromBlockingBefore(AdvisedRequest advisedRequest,
      StreamAroundAdvisorChain chain, Function<AdvisedRequest, AdvisedRequest> beforeAdvise) {

    // This can be executed by both blocking and non-blocking Threads
    // E.g. a command line or Tomcat blocking Thread implementation
    // or by a WebFlux dispatch in a non-blocking manner.
    return (this.protectFromBlocking) ?
    // @formatter:off
      Mono.just(advisedRequest)
        .publishOn(Schedulers.boundedElastic())
        .map(beforeAdvise)
        .flatMapMany(request -> chain.nextAroundStream(request))
      : chain.nextAroundStream(beforeAdvise.apply(advisedRequest));
  }

  //......
}  

AbstractChatMemoryAdvisor声明实现CallAroundAdvisor、StreamAroundAdvisor接口,它有三个实现类,分别是MessageChatMemoryAdvisor、PromptChatMemoryAdvisor、VectorStoreChatMemoryAdvisor

MessageChatMemoryAdvisor

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MessageChatMemoryAdvisor extends AbstractChatMemoryAdvisor<ChatMemory> {

  public MessageChatMemoryAdvisor(ChatMemory chatMemory) {
    super(chatMemory);
  }

  public MessageChatMemoryAdvisor(ChatMemory chatMemory, String defaultConversationId, int chatHistoryWindowSize) {
    this(chatMemory, defaultConversationId, chatHistoryWindowSize, Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER);
  }

  public MessageChatMemoryAdvisor(ChatMemory chatMemory, String defaultConversationId, int chatHistoryWindowSize,
      int order) {
    super(chatMemory, defaultConversationId, chatHistoryWindowSize, true, order);
  }

  public static Builder builder(ChatMemory chatMemory) {
    return new Builder(chatMemory);
  }

  @Override
  public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

    advisedRequest = this.before(advisedRequest);

    AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);

    this.observeAfter(advisedResponse);

    return advisedResponse;
  }

  @Override
  public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

    Flux<AdvisedResponse> advisedResponses = this.doNextWithProtectFromBlockingBefore(advisedRequest, chain,
        this::before);

    return new MessageAggregator().aggregateAdvisedResponse(advisedResponses, this::observeAfter);
  }

  private AdvisedRequest before(AdvisedRequest request) {

    String conversationId = this.doGetConversationId(request.adviseContext());

    int chatMemoryRetrieveSize = this.doGetChatMemoryRetrieveSize(request.adviseContext());

    // 1. Retrieve the chat memory for the current conversation.
    List<Message> memoryMessages = this.getChatMemoryStore().get(conversationId, chatMemoryRetrieveSize);

    // 2. Advise the request messages list.
    List<Message> advisedMessages = new ArrayList<>(request.messages());
    advisedMessages.addAll(memoryMessages);

    // 3. Create a new request with the advised messages.
    AdvisedRequest advisedRequest = AdvisedRequest.from(request).messages(advisedMessages).build();

    // 4. Add the new user input to the conversation memory.
    UserMessage userMessage = new UserMessage(request.userText(), request.media());
    this.getChatMemoryStore().add(this.doGetConversationId(request.adviseContext()), userMessage);

    return advisedRequest;
  }

  private void observeAfter(AdvisedResponse advisedResponse) {

    List<Message> assistantMessages = advisedResponse.response()
      .getResults()
      .stream()
      .map(g -> (Message) g.getOutput())
      .toList();

    this.getChatMemoryStore().add(this.doGetConversationId(advisedResponse.adviseContext()), assistantMessages);
  }

  public static class Builder extends AbstractChatMemoryAdvisor.AbstractBuilder<ChatMemory> {

    protected Builder(ChatMemory chatMemory) {
      super(chatMemory);
    }

    public MessageChatMemoryAdvisor build() {
      return new MessageChatMemoryAdvisor(this.chatMemory, this.conversationId, this.chatMemoryRetrieveSize,
          this.order);
    }

  }

}

MessageChatMemoryAdvisor继承了AbstractChatMemoryAdvisor,其泛型为ChatMemory;其before方法先获取conversationId、chatMemoryRetrieveSize,之后从chatMemoryStore获取memoryMessages,然后将请求的message与memoryMessages一起构造了advisedMessages形成advisedRequest;其observeAfter方法将返回的assistantMessages添加到chatMemoryStore

PromptChatMemoryAdvisor

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class PromptChatMemoryAdvisor extends AbstractChatMemoryAdvisor<ChatMemory> {

  private static final String DEFAULT_SYSTEM_TEXT_ADVISE = """

      Use the conversation memory from the MEMORY section to provide accurate answers.

      ---------------------
      MEMORY:
      {memory}
      ---------------------

      """;

  private final String systemTextAdvise;

  public PromptChatMemoryAdvisor(ChatMemory chatMemory) {
    this(chatMemory, DEFAULT_SYSTEM_TEXT_ADVISE);
  }

  public PromptChatMemoryAdvisor(ChatMemory chatMemory, String systemTextAdvise) {
    super(chatMemory);
    this.systemTextAdvise = systemTextAdvise;
  }

  public PromptChatMemoryAdvisor(ChatMemory chatMemory, String defaultConversationId, int chatHistoryWindowSize,
      String systemTextAdvise) {
    this(chatMemory, defaultConversationId, chatHistoryWindowSize, systemTextAdvise,
        Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER);
  }

  public PromptChatMemoryAdvisor(ChatMemory chatMemory, String defaultConversationId, int chatHistoryWindowSize,
      String systemTextAdvise, int order) {
    super(chatMemory, defaultConversationId, chatHistoryWindowSize, true, order);
    this.systemTextAdvise = systemTextAdvise;
  }

  public static Builder builder(ChatMemory chatMemory) {
    return new Builder(chatMemory);
  }

  @Override
  public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

    advisedRequest = this.before(advisedRequest);

    AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);

    this.observeAfter(advisedResponse);

    return advisedResponse;
  }

  @Override
  public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

    Flux<AdvisedResponse> advisedResponses = this.doNextWithProtectFromBlockingBefore(advisedRequest, chain,
        this::before);

    return new MessageAggregator().aggregateAdvisedResponse(advisedResponses, this::observeAfter);
  }

  private AdvisedRequest before(AdvisedRequest request) {

    // 1. Advise system parameters.
    List<Message> memoryMessages = this.getChatMemoryStore()
      .get(this.doGetConversationId(request.adviseContext()),
          this.doGetChatMemoryRetrieveSize(request.adviseContext()));

    String memory = (memoryMessages != null) ? memoryMessages.stream()
      .filter(m -> m.getMessageType() == MessageType.USER || m.getMessageType() == MessageType.ASSISTANT)
      .map(m -> m.getMessageType() + ":" + ((Content) m).getText())
      .collect(Collectors.joining(System.lineSeparator())) : "";

    Map<String, Object> advisedSystemParams = new HashMap<>(request.systemParams());
    advisedSystemParams.put("memory", memory);

    // 2. Advise the system text.
    String advisedSystemText = request.systemText() + System.lineSeparator() + this.systemTextAdvise;

    // 3. Create a new request with the advised system text and parameters.
    AdvisedRequest advisedRequest = AdvisedRequest.from(request)
      .systemText(advisedSystemText)
      .systemParams(advisedSystemParams)
      .build();

    // 4. Add the new user input to the conversation memory.
    UserMessage userMessage = new UserMessage(request.userText(), request.media());
    this.getChatMemoryStore().add(this.doGetConversationId(request.adviseContext()), userMessage);

    return advisedRequest;
  }

  private void observeAfter(AdvisedResponse advisedResponse) {

    List<Message> assistantMessages = advisedResponse.response()
      .getResults()
      .stream()
      .map(g -> (Message) g.getOutput())
      .toList();

    this.getChatMemoryStore().add(this.doGetConversationId(advisedResponse.adviseContext()), assistantMessages);
  }

  //......

}  

PromptChatMemoryAdvisor继承了AbstractChatMemoryAdvisor,其before方法先从chatMemoryStore获取memoryMessages,过滤出type为USER或者ASSISTANT的,作为memory加入到advisedSystemParams,一起构建AdvisedRequest,同时将userMessage添加到chatMemoryStore中;其observeAfter方法将返回的assistantMessages添加到chatMemoryStore

VectorStoreChatMemoryAdvisor

org/springframework/ai/chat/client/advisor/VectorStoreChatMemoryAdvisor.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class VectorStoreChatMemoryAdvisor extends AbstractChatMemoryAdvisor<VectorStore> {

  private static final String DOCUMENT_METADATA_CONVERSATION_ID = "conversationId";

  private static final String DOCUMENT_METADATA_MESSAGE_TYPE = "messageType";

  private static final String DEFAULT_SYSTEM_TEXT_ADVISE = """

      Use the long term conversation memory from the LONG_TERM_MEMORY section to provide accurate answers.

      ---------------------
      LONG_TERM_MEMORY:
      {long_term_memory}
      ---------------------
      """;

  private final String systemTextAdvise;

  public VectorStoreChatMemoryAdvisor(VectorStore vectorStore) {
    this(vectorStore, DEFAULT_SYSTEM_TEXT_ADVISE);
  }

  public VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String systemTextAdvise) {
    super(vectorStore);
    this.systemTextAdvise = systemTextAdvise;
  }

  public VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId,
      int chatHistoryWindowSize) {
    this(vectorStore, defaultConversationId, chatHistoryWindowSize, DEFAULT_SYSTEM_TEXT_ADVISE);
  }

  public VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId,
      int chatHistoryWindowSize, String systemTextAdvise) {
    this(vectorStore, defaultConversationId, chatHistoryWindowSize, systemTextAdvise,
        Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER);
  }

  /**
   * Constructor for VectorStoreChatMemoryAdvisor.
   * @param vectorStore the vector store instance used for managing and querying
   * documents.
   * @param defaultConversationId the default conversation ID used if none is provided
   * in the context.
   * @param chatHistoryWindowSize the window size for the chat history retrieval.
   * @param systemTextAdvise the system text advice used for the chat advisor system.
   * @param order the order of precedence for this advisor in the chain.
   */
  public VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId,
      int chatHistoryWindowSize, String systemTextAdvise, int order) {
    super(vectorStore, defaultConversationId, chatHistoryWindowSize, true, order);
    this.systemTextAdvise = systemTextAdvise;
  }

  public static Builder builder(VectorStore chatMemory) {
    return new Builder(chatMemory);
  }

  @Override
  public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

    advisedRequest = this.before(advisedRequest);

    AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);

    this.observeAfter(advisedResponse);

    return advisedResponse;
  }

  @Override
  public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

    Flux<AdvisedResponse> advisedResponses = this.doNextWithProtectFromBlockingBefore(advisedRequest, chain,
        this::before);

    // The observeAfter will certainly be executed on non-blocking Threads in case
    // of some models - e.g. when the model client is a WebClient
    return new MessageAggregator().aggregateAdvisedResponse(advisedResponses, this::observeAfter);
  }

  private AdvisedRequest before(AdvisedRequest request) {

    String advisedSystemText;
    if (StringUtils.hasText(request.systemText())) {
      advisedSystemText = request.systemText() + System.lineSeparator() + this.systemTextAdvise;
    }
    else {
      advisedSystemText = this.systemTextAdvise;
    }

    var searchRequest = SearchRequest.builder()
      .query(request.userText())
      .topK(this.doGetChatMemoryRetrieveSize(request.adviseContext()))
      .filterExpression(
          DOCUMENT_METADATA_CONVERSATION_ID + "=='" + this.doGetConversationId(request.adviseContext()) + "'")
      .build();

    List<Document> documents = this.getChatMemoryStore().similaritySearch(searchRequest);

    String longTermMemory = documents.stream()
      .map(Document::getText)
      .collect(Collectors.joining(System.lineSeparator()));

    Map<String, Object> advisedSystemParams = new HashMap<>(request.systemParams());
    advisedSystemParams.put("long_term_memory", longTermMemory);

    AdvisedRequest advisedRequest = AdvisedRequest.from(request)
      .systemText(advisedSystemText)
      .systemParams(advisedSystemParams)
      .build();

    UserMessage userMessage = new UserMessage(request.userText(), request.media());
    this.getChatMemoryStore()
      .write(toDocuments(List.of(userMessage), this.doGetConversationId(request.adviseContext())));

    return advisedRequest;
  }

  private void observeAfter(AdvisedResponse advisedResponse) {

    List<Message> assistantMessages = advisedResponse.response()
      .getResults()
      .stream()
      .map(g -> (Message) g.getOutput())
      .toList();

    this.getChatMemoryStore()
      .write(toDocuments(assistantMessages, this.doGetConversationId(advisedResponse.adviseContext())));
  }

  private List<Document> toDocuments(List<Message> messages, String conversationId) {

    List<Document> docs = messages.stream()
      .filter(m -> m.getMessageType() == MessageType.USER || m.getMessageType() == MessageType.ASSISTANT)
      .map(message -> {
        var metadata = new HashMap<>(message.getMetadata() != null ? message.getMetadata() : new HashMap<>());
        metadata.put(DOCUMENT_METADATA_CONVERSATION_ID, conversationId);
        metadata.put(DOCUMENT_METADATA_MESSAGE_TYPE, message.getMessageType().name());
        if (message instanceof UserMessage userMessage) {
          return Document.builder()
            .text(userMessage.getText())
            // userMessage.getMedia().get(0).getId()
            // TODO vector store for memory would not store this into the
            // vector store, could store an 'id' instead
            // .media(userMessage.getMedia())
            .metadata(metadata)
            .build();
        }
        else if (message instanceof AssistantMessage assistantMessage) {
          return Document.builder().text(assistantMessage.getText()).metadata(metadata).build();
        }
        throw new RuntimeException("Unknown message type: " + message.getMessageType());
      })
      .toList();

    return docs;
  }

  //......

}  

VectorStoreChatMemoryAdvisor继承了AbstractChatMemoryAdvisor,其泛型为VectorStore,其before方法先从构建searchRequest从VectorStore获取topK的documents为long_term_memory加入到advisedSystemParams一起构建advisedRequest,同时将userMessage写入到VectorStore;其observeAfter方法将返回的assistantMessages添加到VectorStore

DefaultChatClient

org/springframework/ai/chat/client/DefaultChatClient.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    private ChatResponse doGetChatResponse(DefaultChatClientRequestSpec inputRequestSpec,
        @Nullable String formatParam, Observation parentObservation) {

      AdvisedRequest advisedRequest = toAdvisedRequest(inputRequestSpec, formatParam);

      // Apply the around advisor chain that terminates with the last model call
      // advisor.
      AdvisedResponse advisedResponse = inputRequestSpec.aroundAdvisorChainBuilder.build()
        .nextAroundCall(advisedRequest);

      return advisedResponse.response();
    }

DefaultChatClient的doGetChatResponse会构建DefaultAroundAdvisorChain然后执行其nextAroundCall方法

DefaultAroundAdvisorChain

org/springframework/ai/chat/client/advisor/DefaultAroundAdvisorChain.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DefaultAroundAdvisorChain implements CallAroundAdvisorChain, StreamAroundAdvisorChain {

  public static final AdvisorObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultAdvisorObservationConvention();

  private final Deque<CallAroundAdvisor> callAroundAdvisors;

  private final Deque<StreamAroundAdvisor> streamAroundAdvisors;

  private final ObservationRegistry observationRegistry;

  DefaultAroundAdvisorChain(ObservationRegistry observationRegistry, Deque<CallAroundAdvisor> callAroundAdvisors,
      Deque<StreamAroundAdvisor> streamAroundAdvisors) {

    Assert.notNull(observationRegistry, "the observationRegistry must be non-null");
    Assert.notNull(callAroundAdvisors, "the callAroundAdvisors must be non-null");
    Assert.notNull(streamAroundAdvisors, "the streamAroundAdvisors must be non-null");

    this.observationRegistry = observationRegistry;
    this.callAroundAdvisors = callAroundAdvisors;
    this.streamAroundAdvisors = streamAroundAdvisors;
  }

  public static Builder builder(ObservationRegistry observationRegistry) {
    return new Builder(observationRegistry);
  }

  @Override
  public AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest) {

    if (this.callAroundAdvisors.isEmpty()) {
      throw new IllegalStateException("No AroundAdvisor available to execute");
    }

    var advisor = this.callAroundAdvisors.pop();

    var observationContext = AdvisorObservationContext.builder()
      .advisorName(advisor.getName())
      .advisorType(AdvisorObservationContext.Type.AROUND)
      .advisedRequest(advisedRequest)
      .advisorRequestContext(advisedRequest.adviseContext())
      .order(advisor.getOrder())
      .build();

    return AdvisorObservationDocumentation.AI_ADVISOR
      .observation(null, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, this.observationRegistry)
      .observe(() -> advisor.aroundCall(advisedRequest, this));
  }

  @Override
  public Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest) {
    return Flux.deferContextual(contextView -> {
      if (this.streamAroundAdvisors.isEmpty()) {
        return Flux.error(new IllegalStateException("No AroundAdvisor available to execute"));
      }

      var advisor = this.streamAroundAdvisors.pop();

      AdvisorObservationContext observationContext = AdvisorObservationContext.builder()
        .advisorName(advisor.getName())
        .advisorType(AdvisorObservationContext.Type.AROUND)
        .advisedRequest(advisedRequest)
        .advisorRequestContext(advisedRequest.adviseContext())
        .order(advisor.getOrder())
        .build();

      var observation = AdvisorObservationDocumentation.AI_ADVISOR.observation(null,
          DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, this.observationRegistry);

      observation.parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null)).start();

      // @formatter:off
      return Flux.defer(() -> advisor.aroundStream(advisedRequest, this))
          .doOnError(observation::error)
          .doFinally(s -> observation.stop())
          .contextWrite(ctx -> ctx.put(ObservationThreadLocalAccessor.KEY, observation));
      // @formatter:on
    });
  }

  public static class Builder {

    private final ObservationRegistry observationRegistry;

    private final Deque<CallAroundAdvisor> callAroundAdvisors;

    private final Deque<StreamAroundAdvisor> streamAroundAdvisors;

    public Builder(ObservationRegistry observationRegistry) {
      this.observationRegistry = observationRegistry;
      this.callAroundAdvisors = new ConcurrentLinkedDeque<>();
      this.streamAroundAdvisors = new ConcurrentLinkedDeque<>();
    }

    public Builder push(Advisor aroundAdvisor) {
      Assert.notNull(aroundAdvisor, "the aroundAdvisor must be non-null");
      return this.pushAll(List.of(aroundAdvisor));
    }

    public Builder pushAll(List<? extends Advisor> advisors) {
      Assert.notNull(advisors, "the advisors must be non-null");
      if (!CollectionUtils.isEmpty(advisors)) {
        List<CallAroundAdvisor> callAroundAdvisorList = advisors.stream()
          .filter(a -> a instanceof CallAroundAdvisor)
          .map(a -> (CallAroundAdvisor) a)
          .toList();

        if (!CollectionUtils.isEmpty(callAroundAdvisorList)) {
          callAroundAdvisorList.forEach(this.callAroundAdvisors::push);
        }

        List<StreamAroundAdvisor> streamAroundAdvisorList = advisors.stream()
          .filter(a -> a instanceof StreamAroundAdvisor)
          .map(a -> (StreamAroundAdvisor) a)
          .toList();

        if (!CollectionUtils.isEmpty(streamAroundAdvisorList)) {
          streamAroundAdvisorList.forEach(this.streamAroundAdvisors::push);
        }

        this.reOrder();
      }
      return this;
    }

    /**
     * (Re)orders the advisors in priority order based on their Ordered attribute.
     */
    private void reOrder() {
      ArrayList<CallAroundAdvisor> callAdvisors = new ArrayList<>(this.callAroundAdvisors);
      OrderComparator.sort(callAdvisors);
      this.callAroundAdvisors.clear();
      callAdvisors.forEach(this.callAroundAdvisors::addLast);

      ArrayList<StreamAroundAdvisor> streamAdvisors = new ArrayList<>(this.streamAroundAdvisors);
      OrderComparator.sort(streamAdvisors);
      this.streamAroundAdvisors.clear();
      streamAdvisors.forEach(this.streamAroundAdvisors::addLast);
    }

    public DefaultAroundAdvisorChain build() {
      return new DefaultAroundAdvisorChain(this.observationRegistry, this.callAroundAdvisors,
          this.streamAroundAdvisors);
    }

  }
}  

DefaultAroundAdvisorChain实现了CallAroundAdvisorChain, StreamAroundAdvisorChain接口,它用Deque类型存储了aroundAdvisors,其nextAroundCall、nextAroundStream会先pop出来当前的advisor,然后执行其aroundCall方法,每个实现类的。 其Builder的push方法每次都会执行reOrder方法对roundAdvisors进行重新排序。

DefaultChatClientRequestSpec

org/springframework/ai/chat/client/DefaultChatClient.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static class DefaultChatClientRequestSpec implements ChatClientRequestSpec {

  //......

    public DefaultChatClientRequestSpec(ChatModel chatModel, @Nullable String userText,
        Map<String, Object> userParams, @Nullable String systemText, Map<String, Object> systemParams,
        List<FunctionCallback> functionCallbacks, List<Message> messages, List<String> functionNames,
        List<Media> media, @Nullable ChatOptions chatOptions, List<Advisor> advisors,
        Map<String, Object> advisorParams, ObservationRegistry observationRegistry,
        @Nullable ChatClientObservationConvention customObservationConvention,
        Map<String, Object> toolContext) {

      Assert.notNull(chatModel, "chatModel cannot be null");
      Assert.notNull(userParams, "userParams cannot be null");
      Assert.notNull(systemParams, "systemParams cannot be null");
      Assert.notNull(functionCallbacks, "functionCallbacks cannot be null");
      Assert.notNull(messages, "messages cannot be null");
      Assert.notNull(functionNames, "functionNames cannot be null");
      Assert.notNull(media, "media cannot be null");
      Assert.notNull(advisors, "advisors cannot be null");
      Assert.notNull(advisorParams, "advisorParams cannot be null");
      Assert.notNull(observationRegistry, "observationRegistry cannot be null");
      Assert.notNull(toolContext, "toolContext cannot be null");

      this.chatModel = chatModel;
      this.chatOptions = chatOptions != null ? chatOptions.copy()
          : (chatModel.getDefaultOptions() != null) ? chatModel.getDefaultOptions().copy() : null;

      this.userText = userText;
      this.userParams.putAll(userParams);
      this.systemText = systemText;
      this.systemParams.putAll(systemParams);

      this.functionNames.addAll(functionNames);
      this.functionCallbacks.addAll(functionCallbacks);
      this.messages.addAll(messages);
      this.media.addAll(media);
      this.advisors.addAll(advisors);
      this.advisorParams.putAll(advisorParams);
      this.observationRegistry = observationRegistry;
      this.customObservationConvention = customObservationConvention != null ? customObservationConvention
          : DEFAULT_CHAT_CLIENT_OBSERVATION_CONVENTION;
      this.toolContext.putAll(toolContext);

      // @formatter:off
      // At the stack bottom add the non-streaming and streaming model call advisors.
      // They play the role of the last advisor in the around advisor chain.
      this.advisors.add(new CallAroundAdvisor() {

        @Override
        public String getName() {
          return CallAroundAdvisor.class.getSimpleName();
        }

        @Override
        public int getOrder() {
          return Ordered.LOWEST_PRECEDENCE;
        }

        @Override
        public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
          return new AdvisedResponse(chatModel.call(advisedRequest.toPrompt()), Collections.unmodifiableMap(advisedRequest.adviseContext()));
        }
      });

      this.advisors.add(new StreamAroundAdvisor() {

        @Override
        public String getName() {
          return StreamAroundAdvisor.class.getSimpleName();
        }

        @Override
        public int getOrder() {
          return Ordered.LOWEST_PRECEDENCE;
        }

        @Override
        public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
          return chatModel.stream(advisedRequest.toPrompt())
          .map(chatResponse -> new AdvisedResponse(chatResponse, Collections.unmodifiableMap(advisedRequest.adviseContext())))
          .publishOn(Schedulers.boundedElastic()); // TODO add option to disable.
        }
      });
      // @formatter:on

      this.aroundAdvisorChainBuilder = DefaultAroundAdvisorChain.builder(observationRegistry)
        .pushAll(this.advisors);
    }

  //......
}

DefaultChatClientRequestSpec会以Ordered.LOWEST_PRECEDENCE的顺序加上最后一道advisor,来终止整个chain,避免调用chain.nextAroundCall(advisedRequest)导致抛出异常。

小结

Spring AI的Advisor提供了类似aop的机制,核心接口是CallAroundAdvisor, StreamAroundAdvisor,它们有SimpleLoggerAdvisor、SafeGuardAdvisor、QuestionAnswerAdvisor、AbstractChatMemoryAdvisor(MessageChatMemoryAdvisorPromptChatMemoryAdvisorVectorStoreChatMemoryAdvisor)、BaseAdvisor(RetrievalAugmentationAdvisor)这些实现。

doc

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
新手别怕!3 分钟学会扣子(coze)基础智能体部署
扣子Coze AI,由字节跳动匠心打造,是一款功能强大的AI智能体开发平台。借助该平台,用户能够轻松创建、灵活配置并便捷发布各类AI智能体(即机器人)。扣子Coze AI搭载了先进的自然语言处理(NLP)技术,这使其具备了广泛的应用潜力,可被用于搭建聊天机器人,实现人机间流畅的对话交流;构建自动化客服系统,高效解决客户咨询问题;开发智能助手,为用户提供贴心的日常事务协助等丰富多样的应用场景 。
意疏
2025/03/20
1.1K0
新手别怕!3 分钟学会扣子(coze)基础智能体部署
飞书多维表格+DeepSeek:生产力大幅度提升
当你还在官网一遍遍重试时,我已经使用飞书多维表格中的DeepSeek R1实现批量化处理信息和任务,效率提升10倍。
AI研思录
2025/02/20
6920
飞书多维表格+DeepSeek:生产力大幅度提升
4个AI提示词模板,帮你躺赢10万+爆文,第三个太狠了!
写了好文章却没人点击?问题可能出在标题上!这个提示词能帮你生成10个吸引眼球的标题方案,激发读者好奇心,让你的文章阅读量瞬间暴涨!
一臻AI
2025/03/29
1470
4个AI提示词模板,帮你躺赢10万+爆文,第三个太狠了!
AI智能体|老板以为我在加班做Excel图表,其实是DeepSeek+扣子(Coze)在帮我!
小王是某电商平台的运营专员,每周一的工作就是整理上周的销售数据,制作可视化报表给领导汇报。
一臻AI
2025/04/28
3821
AI智能体|老板以为我在加班做Excel图表,其实是DeepSeek+扣子(Coze)在帮我!
Deepseek52条喂饭指令
- 指令:以[身份]的口吻,写一篇[产品]的种草笔记,突出5个使用场景和3个痛点解决方案。
程序员皮皮林
2025/02/09
1600
探秘 AI Agent 之 Coze 智能体:插件创建与使用(7/30)
摘要:Coze 智能体作为新一代 AI 应用开发平台,其插件功能发挥着至关重要的作用。本文详细阐述了 Coze 智能体插件的多方面内容。首先介绍平台特点及插件重要性,插件能通过调用外部 API 扩展智能体能力。接着分析其功能扩展优势及与其他功能协同性,在内容创作、数据分析等多领域有广泛应用场景。文中细致讲解了插件创建步骤,从准备工作到具体流程,包括配置信息、设置参数及测试发布等。还说明了插件使用方法,如添加到智能体、结合工作流运用及多插件协同案例。通过不同场景实战案例展示其价值,并分享常见问题解决办法。最后探讨行业趋势,展望插件未来功能拓展方向,为读者深入理解和运用 Coze 智能体插件提供全面指导。
正在走向自律
2024/12/18
1.9K0
探秘 AI Agent 之 Coze 智能体:插件创建与使用(7/30)
【AIGC】ChatGPT提示词Prompt助力自媒体内容创作升级
以下是董宇辉老师的省市作文集关于北京的文章,作为本次例子中的ChatGPT仿写文章:
CSDN-Z
2024/10/17
2780
【AIGC】ChatGPT提示词Prompt助力自媒体内容创作升级
如何用AI创作10W+阅读量的爆款文章?四步轻松达成
撰写一篇能吸引十万加阅读量的爆款文章,需要深入了解这些文章之所以成功的内在因素。每一则广受欢迎的文章背后,都蕴含着精心的设计与技巧。即便对于普通作者来说,只要掌握文章的结构、逻辑、风格以及标题等核心元素的解析方法,便能在模仿与学习的过程中逐步提升个人的写作水平。借助现代技术的力量,如人工智能(AI)的帮助,分析与仿写文章的过程将变得更加便捷。
凯哥Java
2024/11/27
3160
如何用AI创作10W+阅读量的爆款文章?四步轻松达成
【愚公系列】《高效使用DeepSeek》025-科普内容生成
📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主!
愚公搬代码
2025/04/01
1700
告别加班!用DeepSeek搭建全自动爆款图文工厂
当传统内容生产者仍在手工打磨作品时,人工智能技术正以量子级内容生成力重塑行业规则。
云惑雨问
2025/03/17
2200
告别加班!用DeepSeek搭建全自动爆款图文工厂
Coze bot 使用&变现手册,核心是动起来
今天,我们就来聊一聊字节跳动刚推出的一个非常酷的平台——coze.cn。这个平台简直是为编程小白和资深程序员都准备的“AI梦工厂”。(体验过,0代码,比 GPTs 配置还顺畅,还可以自己的知识库拓展等等功能)
老表
2024/02/06
5.4K0
Coze bot 使用&变现手册,核心是动起来
【AIGC】ChatGPT提示词Prompt解析:如何生成爆款标题、节日热点文案与完美文字排版
随着ChatGPT的不断发展与进化,未来它在内容创作中的应用将更加广泛和智能化。从生成情绪化爆款标题到紧跟节日热点的文案创作,再到优化排版技巧,ChatGPT不仅能够帮助创作者更高效地完成任务,还能通过数据分析和深度学习不断提升内容的精准度和个性化。
CSDN-Z
2024/10/17
4330
【AIGC】ChatGPT提示词Prompt解析:如何生成爆款标题、节日热点文案与完美文字排版
龙哥风向标20231017 GPT拆解
盈利点:利用小红书博主发布的笔记和销量信息,可以进行服装短视频带货或图文带货的模仿学习,从中获取盈利。
ApacheCN_飞龙
2024/01/31
1660
DeepSeek 技术创作者 10 倍速构建专业影响力 —— 现场回顾
大家好,我是默语!3 月 9 日,我在杭州参加了 线下活动,与众多 AI 技术创作者、专家和行业大咖们齐聚一堂,深入探讨 AI 时代如何快速构建个人 IP,并高效进行技术创作。
默 语
2025/03/10
1010
DeepSeek 技术创作者 10 倍速构建专业影响力 —— 现场回顾
探秘 AI Agent 之 Coze 智能体:从简介到搭建全攻略(4/30)
Coze 智能体是基于机器学习和自然语言处理技术的软件实体,它在人工智能领域扮演着重要的角色,能够像一个智能助手一样,通过与外界环境进行交互学习,进而执行各种各样的任务。
正在走向自律
2024/12/18
10K0
探秘 AI Agent 之 Coze 智能体:从简介到搭建全攻略(4/30)
Coze 扣子 | AI 养育计划 - &#34;Flutter 大师&#34;
扣子(coze.cn)是一款用来开发新一代 AI Chat Bot 的应用编辑平台。其中可以构建自己的知识库以及作为资源,这样的话,让 AI Bot 拥有我所有文章的 "智慧",岂不是一位 Flutter 大师 嘛。毕竟连我自己可能都记不清,很久以前文章里的知识细节,让用户和这种 "知识怪物" 交流,肯定能有意料之外的能力。
张风捷特烈
2024/02/25
5310
Coze 扣子 |  AI 养育计划 - &#34;Flutter 大师&#34;
龙哥风向标20231107 GPT拆解
盈利点:利用朋友圈富婆关注疗愈赛道的商机,可以通过直播销售疗愈产品或服务,吸引富裕女性客户,实现高额销售额。
ApacheCN_飞龙
2024/01/31
1640
探秘 AI Agent 之 Coze 智能体:工作流模式(6/30)
摘要:在人工智能领域,随着大模型技术的迅猛发展,智能体的能力得到了极大提升。然而,现实世界中的工作任务往往复杂多样,仅依靠单次大模型调用已难以满足需求。许多任务需要多个步骤、多种技术的协同配合才能完成,例如从数据收集到分析再到决策的过程,或者从用户需求理解到多轮交互并最终提供满意解决方案的服务场景。工作流模式应运而生,它将复杂任务分解为一系列有序的子任务,通过合理组织和协调不同的组件与操作,实现任务的高效处理。这种模式在当前的 AI 应用开发中具有至关重要的意义,它不仅提高了任务处理的准确性和可靠性,还增强了智能体应对复杂多变环境的能力。
正在走向自律
2024/12/18
4.9K2
探秘 AI Agent 之 Coze 智能体:工作流模式(6/30)
【AIGC】ChatGPT提示词Prompt助力广告文案、PPT制作与书籍推荐的高效新模式
本文通过详细介绍了如何使用ChatGPT生成多种类型的内容,包括朋友圈广告推销文案、PPT演示文稿以及书籍推荐爆款文案。利用AI工具,通过精准的提示词和用户的反馈,可以高效提取关键信息,生成具备吸引力、情感共鸣和逻辑清晰的文案或内容框架。这不仅提升了创作效率,还确保了内容质量,适用于各类场景如社交媒体推广、教学演示和营销推广。借助ChatGPT,创作者能够更加专注于优化内容的表达和用户体验。
CSDN-Z
2024/10/17
4420
【AIGC】ChatGPT提示词Prompt助力广告文案、PPT制作与书籍推荐的高效新模式
龙哥风向标20231219 GPT拆解
盈利点:利用小红书热帖中雍和宫手串助力考研上岸的需求火爆,可以代理雍和宫手串并进行销售,同时可以拓展更多与本命年相关产品,如开光手串等,以满足用户需求。
ApacheCN_飞龙
2024/01/31
1560
推荐阅读
相关推荐
新手别怕!3 分钟学会扣子(coze)基础智能体部署
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • Advisor
    • CallAroundAdvisor
    • StreamAroundAdvisor
  • SimpleLoggerAdvisor
  • SafeGuardAdvisor
  • QuestionAnswerAdvisor
  • AbstractChatMemoryAdvisor
    • MessageChatMemoryAdvisor
    • PromptChatMemoryAdvisor
    • VectorStoreChatMemoryAdvisor
  • DefaultChatClient
    • DefaultAroundAdvisorChain
    • DefaultChatClientRequestSpec
  • 小结
  • doc
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档