本案例將引導您一步步構建一個基於 Spring AI Alibaba 的 Graph Reflection 應用,演示如何利用 Graph 和 ReflectAgent 實現一個能夠自我反思和改進的 AI 助手,特別適用於寫作和內容創作場景。

1. 案例目標

我們將創建一個包含自我反思和改進功能的 Web 應用:

  1. AI 寫作助手 (/reflection/chat):通過 Graph 和 ReflectAgent 實現,AI 能夠生成內容並進行自我反思和改進。
  2. 多輪反思機制:AI 生成內容後,會自動進入反思環節,對內容進行評估和改進,最多進行 2 輪反思。
  3. 角色分離設計:將 AI 分為"助手"和"評估者"兩個角色,分別負責內容生成和內容評估,實現更高質量的輸出。

2. 技術棧與核心依賴

  • Spring Boot 3.x
  • Spring AI Alibaba (用於對接阿里雲 DashScope 通義大模型)
  • Spring AI Alibaba Graph (用於構建 AI 工作流)
  • Maven (項目構建工具)

在 pom.xml 中,你需要引入以下核心依賴:

com.alibaba.cloud.aispring-ai-alibaba-starter-dashscopeorg.springframework.aispring-ai-autoconfigure-model-chat-clientcom.alibaba.cloud.aispring-ai-alibaba-graph-core${spring-ai-alibaba.version}org.springframework.bootspring-boot-starter-weborg.apache.httpcomponents.client5httpclient55.4.1org.springframework.aispring-ai-openai

3. 項目配置

在 src/main/resources/application.yml 文件中,配置你的 DashScope API Key 和模型設置。

server:
  port: 8080
spring:
  application:
    name: reflection
  ai:
    dashscope:
      api-key: ${AI_DASHSCOPE_API_KEY}
    openai:
      base-url: https://dashscope.aliyuncs.com/compatible-mode
      api-key: ${AI_DASHSCOPE_API_KEY}
      chat:
        options:
          model: qwen-max-latest
      embedding:
        options:
          model: text-embedding-v1

重要提示:請將 AI_DASHSCOPE_API_KEY 環境變量設置為你從阿里雲獲取的有效 API Key。你也可以直接將其寫在配置文件中,但這不推薦用於生產環境。

4. 項目結構

本案例的項目結構如下:

spring-ai-alibaba-graph-example/reflection/
├── pom.xml                                    # Maven 項目配置文件
├── src/
│   └── main/
│       ├── java/
│       │   └── com/alibaba/cloud/ai/graph/reflection/
│       │       ├── ReflectionApplication.java      # Spring Boot 主程序
│       │       ├── ReflectionController.java       # REST API 控制器
│       │       └── RelectionAutoconfiguration.java  # 自動配置類
│       └── resources/
│           └── application.yml                  # 應用配置文件
└── target/                                    # 構建輸出目錄

5. 核心組件實現

5.1 主程序類 ReflectionApplication.java

這是 Spring Boot 應用的入口點:

package com.alibaba.cloud.ai.graph.reflection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ReflectionApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReflectionApplication.class, args);
    }
}

5.2 REST API 控制器 ReflectionController.java

提供 HTTP API 接口,用於與用户交互:

package com.alibaba.cloud.ai.graph.reflection;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.agent.ReflectAgent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.MessageType;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/reflection")
public class ReflectionController {
    private static final Logger logger = LoggerFactory.getLogger(ReflectionController.class);
    private CompiledGraph compiledGraph;
    public ReflectionController(@Qualifier("reflectGraph") CompiledGraph compiledGraph) {
        this.compiledGraph = compiledGraph;
    }
    @GetMapping("/chat")
    public String simpleChat(String query) throws GraphRunnerException {
        return compiledGraph.invoke(Map.of(ReflectAgent.MESSAGES, List.of(new UserMessage(query))))
            .get()
            .>value(ReflectAgent.MESSAGES)
            .orElseThrow()
            .stream()
            .filter(message -> message.getMessageType() == MessageType.ASSISTANT)
            .reduce((first, second) -> second)
            .map(Message::getText)
            .orElseThrow();
    }
}

5.3 自動配置類 RelectionAutoconfiguration.java

這是本案例的核心,定義了 Graph 和 ReflectAgent 的配置:

5.3.1 助手節點 (AssistantGraphNode)

負責生成內容的節點:

public static class AssistantGraphNode implements NodeAction {
    private final LlmNode llmNode;
    private SystemPromptTemplate systemPromptTemplate;
    private final String NODE_ID = "call_model";
    private static final String CLASSIFIER_PROMPT_TEMPLATE = """
                You are an essay assistant tasked with writing excellent 5-paragraph essays.
                Generate the best essay possible for the user's request.
                If the user provides critique, respond with a revised version of your previous attempts.
                Only return the main content I need, without adding any other interactive language.
                Please answer in Chinese:
            """;
    public AssistantGraphNode(ChatClient chatClient) {
        this.systemPromptTemplate = new SystemPromptTemplate(CLASSIFIER_PROMPT_TEMPLATE);
        this.llmNode = LlmNode.builder()
            .systemPromptTemplate(systemPromptTemplate.render())
            .chatClient(chatClient)
            .messagesKey("messages")
            .build();
    }
    // ... Builder 模式實現 ...
    @Override
    public Map apply(OverAllState overAllState) throws Exception {
        List messages = (List) overAllState.value(ReflectAgent.MESSAGES).get();
        OverAllStateFactory stateFactory = () -> {
            OverAllState state = new OverAllState();
            state.registerKeyAndStrategy(ReflectAgent.MESSAGES, new AppendStrategy());
            return state;
        };
        StateGraph stateGraph = new StateGraph(stateFactory)
            .addNode(this.NODE_ID, AsyncNodeAction.node_async(llmNode))
            .addEdge(StateGraph.START, this.NODE_ID)
            .addEdge(this.NODE_ID, StateGraph.END);
        OverAllState invokeState = stateGraph.compile().invoke(Map.of(ReflectAgent.MESSAGES, messages)).get();
        List reactMessages = (List) invokeState.value(ReflectAgent.MESSAGES).orElseThrow();
        return Map.of(ReflectAgent.MESSAGES, reactMessages);
    }
},>
5.3.2 評估節點 (JudgeGraphNode)

負責評估和提供反饋的節點:

public static class JudgeGraphNode implements NodeAction {
    private final LlmNode llmNode;
    private final String NODE_ID = "judge_response";
    private SystemPromptTemplate systemPromptTemplate;
    private static final String CLASSIFIER_PROMPT_TEMPLATE = """
                You are a teacher grading a student's essay submission. Provide detailed feedback and revision suggestions for the essay.
                Your feedback should cover the following aspects:
                - Length : Is the essay sufficiently developed? Does it meet the required length or need expansion/shortening?
                - Depth : Are the ideas well-developed? Is there sufficient analysis, evidence, or explanation?
                - Structure : Is the organization logical and clear? Are the introduction, transitions, and conclusion effective?
                - Style and Tone : Is the writing style appropriate for the purpose and audience? Is the tone consistent and professional?
                - Language Use : Are vocabulary, grammar, and sentence structure accurate and varied?
                - Focus only on providing actionable suggestions for improvement. Do not include grades, scores, or overall summary evaluations.
                Please respond in Chinese .
            """;
    public JudgeGraphNode(ChatClient chatClient) {
        this.systemPromptTemplate = new SystemPromptTemplate(CLASSIFIER_PROMPT_TEMPLATE);
        this.llmNode = LlmNode.builder()
            .chatClient(chatClient)
            .systemPromptTemplate(systemPromptTemplate.render())
            .messagesKey(ReflectAgent.MESSAGES)
            .build();
    }
    // ... Builder 模式實現 ...
    @Override
    public Map apply(OverAllState allState) throws Exception {
        List messages = (List) allState.value(ReflectAgent.MESSAGES).get();
        OverAllStateFactory stateFactory = () -> {
            OverAllState state = new OverAllState();
            state.registerKeyAndStrategy(ReflectAgent.MESSAGES, new AppendStrategy());
            return state;
        };
        StateGraph stateGraph = new StateGraph(stateFactory)
            .addNode(this.NODE_ID, AsyncNodeAction.node_async(llmNode))
            .addEdge(StateGraph.START, this.NODE_ID)
            .addEdge(this.NODE_ID, StateGraph.END);
        CompiledGraph compile = stateGraph.compile();
        OverAllState invokeState = compile.invoke(Map.of(ReflectAgent.MESSAGES, messages)).get();
        UnaryOperator> convertLastToUserMessage = messageList -> {
            int size = messageList.size();
            if (size == 0)
                return messageList;
            Message last = messageList.get(size - 1);
            messageList.set(size - 1, new UserMessage(last.getText()));
            return messageList;
        };
        List reactMessages = (List) invokeState.value(ReflectAgent.MESSAGES).orElseThrow();
        convertLastToUserMessage.apply(reactMessages);
        return Map.of(ReflectAgent.MESSAGES, reactMessages);
    }
}
 ,>
5.3.3 ReflectAgent 配置

將助手節點和評估節點組合成 ReflectAgent:

@Bean
public CompiledGraph reflectGraph(ChatModel chatModel) throws GraphStateException {
    ChatClient chatClient = ChatClient.builder(chatModel)
        .defaultAdvisors(new SimpleLoggerAdvisor())
        .defaultOptions(OpenAiChatOptions.builder().internalToolExecutionEnabled(false).build())
        .build();
    AssistantGraphNode assistantGraphNode = AssistantGraphNode.builder().chatClient(chatClient).build();
    JudgeGraphNode judgeGraphNode = JudgeGraphNode.builder().chatClient(chatClient).build();
    ReflectAgent reflectAgent = ReflectAgent.builder()
        .graph(assistantGraphNode)
        .reflection(judgeGraphNode)
        .maxIterations(2)
        .build();
    return reflectAgent.getAndCompileGraph();
}

6. 運行與測試

  1. 啓動應用:運行 ReflectionApplication 主程序。
  2. 使用瀏覽器或 API 工具(如 Postman, curl)進行測試

測試示例

訪問以下 URL,讓 AI 寫一篇關於"人工智能的發展"的文章:

http://localhost:8080/reflection/chat?query=請寫一篇關於人工智能發展的短文

預期響應

AI 將首先生成一篇關於人工智能發展的短文,然後自動進行反思和改進,最終輸出一個經過自我優化的版本。整個過程包含多輪交互,但用户只需要發送一次請求,系統會自動完成反思和改進的過程。

7. 實現思路與擴展建議

實現思路

本案例的核心思想是"自我反思與改進"。通過將 AI 的角色分為"內容生成者"和"內容評估者",實現了:

  • 角色分離:生成者和評估者各司其職,避免了單一角色既要生成內容又要評估內容的侷限性。
  • 多輪迭代:通過設置最大迭代次數,允許 AI 進行多輪反思和改進,逐步提升內容質量。
  • 自動化流程:用户只需提供初始請求,系統自動完成生成、評估、改進的整個流程。

擴展建議

  • 自定義反思提示詞:可以根據不同場景調整評估節點的提示詞,例如針對代碼質量、創意寫作、技術文檔等不同領域。
  • 動態迭代次數:可以根據內容質量或用户需求動態調整迭代次數,而不是固定的最大值。
  • 多角色協作:可以擴展為多個角色的協作,例如"創意生成者"、"邏輯審核者"、"語言優化者"等。
  • 用户干預機制:允許用户在反思過程中進行干預,例如提供額外的反饋或調整方向。
  • 性能優化:對於複雜場景,可以考慮並行處理或緩存機制,提高響應速度。