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

本案例将引导您一步步构建一个基于 Spring AI Alibaba 的 Graph Reflection 应用,演示如何利用 Graph 和 ReflectAgent 实现一个能够自我反思和改进的 AI 助手,特别适用于写作和内容创作场景。
1. 案例目标
我们将创建一个包含自我反思和改进功能的 Web 应用:
- AI 写作助手 (
/reflection/chat):通过 Graph 和 ReflectAgent 实现,AI 能够生成内容并进行自我反思和改进。 - 多轮反思机制:AI 生成内容后,会自动进入反思环节,对内容进行评估和改进,最多进行 2 轮反思。
- 角色分离设计:将 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. 运行与测试
- 启动应用:运行
ReflectionApplication主程序。 - 使用浏览器或 API 工具(如 Postman, curl)进行测试。
测试示例
访问以下 URL,让 AI 写一篇关于"人工智能的发展"的文章:
http://localhost:8080/reflection/chat?query=请写一篇关于人工智能发展的短文
预期响应:
AI 将首先生成一篇关于人工智能发展的短文,然后自动进行反思和改进,最终输出一个经过自我优化的版本。整个过程包含多轮交互,但用户只需要发送一次请求,系统会自动完成反思和改进的过程。
7. 实现思路与扩展建议
实现思路
本案例的核心思想是"自我反思与改进"。通过将 AI 的角色分为"内容生成者"和"内容评估者",实现了:
- 角色分离:生成者和评估者各司其职,避免了单一角色既要生成内容又要评估内容的局限性。
- 多轮迭代:通过设置最大迭代次数,允许 AI 进行多轮反思和改进,逐步提升内容质量。
- 自动化流程:用户只需提供初始请求,系统自动完成生成、评估、改进的整个流程。
扩展建议
- 自定义反思提示词:可以根据不同场景调整评估节点的提示词,例如针对代码质量、创意写作、技术文档等不同领域。
- 动态迭代次数:可以根据内容质量或用户需求动态调整迭代次数,而不是固定的最大值。
- 多角色协作:可以扩展为多个角色的协作,例如"创意生成者"、"逻辑审核者"、"语言优化者"等。
- 用户干预机制:允许用户在反思过程中进行干预,例如提供额外的反馈或调整方向。
- 性能优化:对于复杂场景,可以考虑并行处理或缓存机制,提高响应速度。
浙公网安备 33010602011771号