501-Spring AI Alibaba Graph Reflection 机制完整案例

本案例将引导您一步步构建一个基于 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.ai
        spring-ai-alibaba-starter-dashscope
    
    
    
        org.springframework.ai
        spring-ai-autoconfigure-model-chat-client
    
    
    
        com.alibaba.cloud.ai
        spring-ai-alibaba-graph-core
        ${spring-ai-alibaba.version}
    
    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
    
        org.apache.httpcomponents.client5
        httpclient5
        5.4.1
    
    
    
        org.springframework.ai
        spring-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 进行多轮反思和改进,逐步提升内容质量。
  • 自动化流程:用户只需提供初始请求,系统自动完成生成、评估、改进的整个流程。

扩展建议

  • 自定义反思提示词:可以根据不同场景调整评估节点的提示词,例如针对代码质量、创意写作、技术文档等不同领域。
  • 动态迭代次数:可以根据内容质量或用户需求动态调整迭代次数,而不是固定的最大值。
  • 多角色协作:可以扩展为多个角色的协作,例如"创意生成者"、"逻辑审核者"、"语言优化者"等。
  • 用户干预机制:允许用户在反思过程中进行干预,例如提供额外的反馈或调整方向。
  • 性能优化:对于复杂场景,可以考虑并行处理或缓存机制,提高响应速度。
posted on 2025-11-26 21:06  ljbguanli  阅读(0)  评论(0)    收藏  举报