本案例將引導您一步步構建一個基於 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.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. 運行與測試
- 啓動應用:運行
ReflectionApplication主程序。 - 使用瀏覽器或 API 工具(如 Postman, curl)進行測試。
測試示例
訪問以下 URL,讓 AI 寫一篇關於"人工智能的發展"的文章:
http://localhost:8080/reflection/chat?query=請寫一篇關於人工智能發展的短文
預期響應:
AI 將首先生成一篇關於人工智能發展的短文,然後自動進行反思和改進,最終輸出一個經過自我優化的版本。整個過程包含多輪交互,但用户只需要發送一次請求,系統會自動完成反思和改進的過程。
7. 實現思路與擴展建議
實現思路
本案例的核心思想是"自我反思與改進"。通過將 AI 的角色分為"內容生成者"和"內容評估者",實現了:
- 角色分離:生成者和評估者各司其職,避免了單一角色既要生成內容又要評估內容的侷限性。
- 多輪迭代:通過設置最大迭代次數,允許 AI 進行多輪反思和改進,逐步提升內容質量。
- 自動化流程:用户只需提供初始請求,系統自動完成生成、評估、改進的整個流程。
擴展建議
- 自定義反思提示詞:可以根據不同場景調整評估節點的提示詞,例如針對代碼質量、創意寫作、技術文檔等不同領域。
- 動態迭代次數:可以根據內容質量或用户需求動態調整迭代次數,而不是固定的最大值。
- 多角色協作:可以擴展為多個角色的協作,例如"創意生成者"、"邏輯審核者"、"語言優化者"等。
- 用户干預機制:允許用户在反思過程中進行干預,例如提供額外的反饋或調整方向。
- 性能優化:對於複雜場景,可以考慮並行處理或緩存機制,提高響應速度。