智能体完成
This commit is contained in:
parent
ba744a5f3d
commit
001448fb5b
9
pom.xml
9
pom.xml
@ -20,15 +20,6 @@
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<modules>
|
||||
<module>long-image-search-mcp-server</module>
|
||||
</modules>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
</properties>
|
||||
|
108
src/main/java/com/huangge1199/aiagent/agent/BaseAgent.java
Normal file
108
src/main/java/com/huangge1199/aiagent/agent/BaseAgent.java
Normal file
@ -0,0 +1,108 @@
|
||||
package com.huangge1199.aiagent.agent;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.huangge1199.aiagent.agent.model.AgentState;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BaseAgent
|
||||
* <p>
|
||||
* 抽象基础代理类,用于管理代理状态和执行流程。
|
||||
* <p>
|
||||
* 提供状态转换、内存管理和基于步骤的执行循环的基础功能。
|
||||
* 子类必须实现step方法。
|
||||
*
|
||||
* @author huangge1199
|
||||
* @since 2025/6/4 17:01:45
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
public abstract class BaseAgent {
|
||||
|
||||
// 核心属性
|
||||
private String name;
|
||||
|
||||
// 提示
|
||||
private String systemPrompt;
|
||||
private String nextStepPrompt;
|
||||
|
||||
// 状态
|
||||
private AgentState state = AgentState.IDLE;
|
||||
|
||||
// 执行控制
|
||||
private int maxSteps = 10;
|
||||
private int currentStep = 0;
|
||||
|
||||
// LLM
|
||||
private ChatClient chatClient;
|
||||
|
||||
// Memory(需要自主维护会话上下文)
|
||||
private List<Message> messageList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 运行代理
|
||||
*
|
||||
* @param userPrompt 用户提示词
|
||||
* @return 执行结果
|
||||
*/
|
||||
public String run(String userPrompt) {
|
||||
if (this.state != AgentState.IDLE) {
|
||||
throw new RuntimeException("Cannot run agent from state: " + this.state);
|
||||
}
|
||||
if (StrUtil.isBlank(userPrompt)) {
|
||||
throw new RuntimeException("Cannot run agent with empty user prompt");
|
||||
}
|
||||
// 更改状态
|
||||
state = AgentState.RUNNING;
|
||||
// 记录消息上下文
|
||||
messageList.add(new UserMessage(userPrompt));
|
||||
// 保存结果列表
|
||||
List<String> results = new ArrayList<>();
|
||||
try {
|
||||
for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) {
|
||||
int stepNumber = i + 1;
|
||||
currentStep = stepNumber;
|
||||
log.info("Executing step {}/{}", stepNumber, maxSteps);
|
||||
// 单步执行
|
||||
String stepResult = step();
|
||||
String result = "Step " + stepNumber + ": " + stepResult;
|
||||
results.add(result);
|
||||
}
|
||||
// 检查是否超出步骤限制
|
||||
if (currentStep >= maxSteps) {
|
||||
state = AgentState.FINISHED;
|
||||
results.add("Terminated: Reached max steps (" + maxSteps + ")");
|
||||
}
|
||||
return String.join("\n", results);
|
||||
} catch (Exception e) {
|
||||
state = AgentState.ERROR;
|
||||
log.error("Error executing agent", e);
|
||||
return "执行错误" + e.getMessage();
|
||||
} finally {
|
||||
// 清理资源
|
||||
this.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行单个步骤
|
||||
*
|
||||
* @return 步骤执行结果
|
||||
*/
|
||||
public abstract String step();
|
||||
|
||||
/**
|
||||
* 清理资源
|
||||
*/
|
||||
protected void cleanup() {
|
||||
// 子类可以重写此方法来清理资源
|
||||
}
|
||||
}
|
||||
|
41
src/main/java/com/huangge1199/aiagent/agent/LongManus.java
Normal file
41
src/main/java/com/huangge1199/aiagent/agent/LongManus.java
Normal file
@ -0,0 +1,41 @@
|
||||
package com.huangge1199.aiagent.agent;
|
||||
|
||||
import com.huangge1199.aiagent.config.MyLoggerAdvisor;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.tool.ToolCallback;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* LongManus
|
||||
* AI 超级智能体(拥有自主规划能力,可以直接使用)
|
||||
*
|
||||
* @author huangge1199
|
||||
* @since 2025/6/4 17:08:52
|
||||
*/
|
||||
@Component
|
||||
public class LongManus extends ToolCallAgent {
|
||||
|
||||
public LongManus(ToolCallback[] allTools, ChatModel dashscopeChatModel) {
|
||||
super(allTools);
|
||||
this.setName("longManus");
|
||||
String SYSTEM_PROMPT = """
|
||||
You are LongManus, an all-capable AI assistant, aimed at solving any task presented by the user.
|
||||
You have various tools at your disposal that you can call upon to efficiently complete complex requests.
|
||||
""";
|
||||
this.setSystemPrompt(SYSTEM_PROMPT);
|
||||
String NEXT_STEP_PROMPT = """
|
||||
Based on user needs, proactively select the most appropriate tool or combination of tools.
|
||||
For complex tasks, you can break down the problem and use different tools step by step to solve it.
|
||||
After using each tool, clearly explain the execution results and suggest the next steps.
|
||||
If you want to stop the interaction at any point, use the `terminate` tool/function call.
|
||||
""";
|
||||
this.setNextStepPrompt(NEXT_STEP_PROMPT);
|
||||
this.setMaxSteps(5);
|
||||
// 初始化 AI 对话客户端
|
||||
ChatClient chatClient = ChatClient.builder(dashscopeChatModel)
|
||||
.defaultAdvisors(new MyLoggerAdvisor())
|
||||
.build();
|
||||
this.setChatClient(chatClient);
|
||||
}
|
||||
}
|
57
src/main/java/com/huangge1199/aiagent/agent/ReActAgent.java
Normal file
57
src/main/java/com/huangge1199/aiagent/agent/ReActAgent.java
Normal file
@ -0,0 +1,57 @@
|
||||
package com.huangge1199.aiagent.agent;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* ReActAgent
|
||||
* ReAct (Reasoning and Acting) 模式的代理抽象类
|
||||
* 实现了思考-行动的循环模式
|
||||
*
|
||||
* @author huangge1199
|
||||
* @since 2025/6/4 17:04:56
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Slf4j
|
||||
public abstract class ReActAgent extends BaseAgent {
|
||||
|
||||
/**
|
||||
* 处理当前状态并决定下一步行动
|
||||
*
|
||||
* @return 是否需要执行行动,true表示需要执行,false表示不需要执行
|
||||
*/
|
||||
public abstract boolean think();
|
||||
|
||||
/**
|
||||
* 执行决定的行动
|
||||
*
|
||||
* @return 行动执行结果
|
||||
*/
|
||||
public abstract String act();
|
||||
|
||||
/**
|
||||
* 执行单个步骤:思考和行动
|
||||
*
|
||||
* @return 步骤执行结果
|
||||
*/
|
||||
@Override
|
||||
public String step() {
|
||||
try {
|
||||
// 先思考
|
||||
boolean shouldAct = think();
|
||||
if (!shouldAct) {
|
||||
return "思考完成 - 无需行动";
|
||||
}
|
||||
// 再行动
|
||||
return act();
|
||||
} catch (Exception e) {
|
||||
// 记录异常日志
|
||||
e.printStackTrace();
|
||||
return "步骤执行失败:" + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
140
src/main/java/com/huangge1199/aiagent/agent/ToolCallAgent.java
Normal file
140
src/main/java/com/huangge1199/aiagent/agent/ToolCallAgent.java
Normal file
@ -0,0 +1,140 @@
|
||||
package com.huangge1199.aiagent.agent;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
|
||||
import com.huangge1199.aiagent.agent.model.AgentState;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.ToolResponseMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.tool.ToolCallingManager;
|
||||
import org.springframework.ai.model.tool.ToolExecutionResult;
|
||||
import org.springframework.ai.tool.ToolCallback;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ToolCallAgent
|
||||
* 处理工具调用的基础代理类,具体实现了 think 和 act 方法,可以用作创建实例的父类
|
||||
*
|
||||
* @author huangge1199
|
||||
* @since 2025/6/4 17:05:58
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Slf4j
|
||||
public class ToolCallAgent extends ReActAgent {
|
||||
|
||||
// 可用的工具
|
||||
private final ToolCallback[] availableTools;
|
||||
|
||||
// 保存工具调用信息的响应结果(要调用那些工具)
|
||||
private ChatResponse toolCallChatResponse;
|
||||
|
||||
// 工具调用管理者
|
||||
private final ToolCallingManager toolCallingManager;
|
||||
|
||||
// 禁用 Spring AI 内置的工具调用机制,自己维护选项和消息上下文
|
||||
private final ChatOptions chatOptions;
|
||||
|
||||
public ToolCallAgent(ToolCallback[] availableTools) {
|
||||
super();
|
||||
this.availableTools = availableTools;
|
||||
this.toolCallingManager = ToolCallingManager.builder().build();
|
||||
// 禁用 Spring AI 内置的工具调用机制,自己维护选项和消息上下文
|
||||
this.chatOptions = DashScopeChatOptions.builder()
|
||||
.withProxyToolCalls(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理当前状态并决定下一步行动
|
||||
*
|
||||
* @return 是否需要执行行动
|
||||
*/
|
||||
@Override
|
||||
public boolean think() {
|
||||
// 1、校验提示词,拼接用户提示词
|
||||
if (StrUtil.isNotBlank(getNextStepPrompt())) {
|
||||
UserMessage userMessage = new UserMessage(getNextStepPrompt());
|
||||
getMessageList().add(userMessage);
|
||||
}
|
||||
// 2、调用 AI 大模型,获取工具调用结果
|
||||
List<Message> messageList = getMessageList();
|
||||
Prompt prompt = new Prompt(messageList, this.chatOptions);
|
||||
try {
|
||||
ChatResponse chatResponse = getChatClient().prompt(prompt)
|
||||
.system(getSystemPrompt())
|
||||
.tools(availableTools)
|
||||
.call()
|
||||
.chatResponse();
|
||||
// 记录响应,用于等下 Act
|
||||
this.toolCallChatResponse = chatResponse;
|
||||
// 3、解析工具调用结果,获取要调用的工具
|
||||
// 助手消息
|
||||
AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
|
||||
// 获取要调用的工具列表
|
||||
List<AssistantMessage.ToolCall> toolCallList = assistantMessage.getToolCalls();
|
||||
// 输出提示信息
|
||||
String result = assistantMessage.getText();
|
||||
log.info("{}的思考:{}", getName(), result);
|
||||
log.info("{}选择了 {} 个工具来使用", getName(), toolCallList.size());
|
||||
String toolCallInfo = toolCallList.stream()
|
||||
.map(toolCall -> String.format("工具名称:%s,参数:%s", toolCall.name(), toolCall.arguments()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
log.info(toolCallInfo);
|
||||
// 如果不需要调用工具,返回 false
|
||||
if (toolCallList.isEmpty()) {
|
||||
// 只有不调用工具时,才需要手动记录助手消息
|
||||
getMessageList().add(assistantMessage);
|
||||
return false;
|
||||
} else {
|
||||
// 需要调用工具时,无需记录助手消息,因为调用工具时会自动记录
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("{}的思考过程遇到了问题:{}", getName(), e.getMessage());
|
||||
getMessageList().add(new AssistantMessage("处理时遇到了错误:" + e.getMessage()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行工具调用并处理结果
|
||||
*
|
||||
* @return 执行结果
|
||||
*/
|
||||
@Override
|
||||
public String act() {
|
||||
if (!toolCallChatResponse.hasToolCalls()) {
|
||||
return "没有工具需要调用";
|
||||
}
|
||||
// 调用工具
|
||||
Prompt prompt = new Prompt(getMessageList(), this.chatOptions);
|
||||
ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, toolCallChatResponse);
|
||||
// 记录消息上下文,conversationHistory 已经包含了助手消息和工具调用返回的结果
|
||||
setMessageList(toolExecutionResult.conversationHistory());
|
||||
ToolResponseMessage toolResponseMessage = (ToolResponseMessage) CollUtil.getLast(toolExecutionResult.conversationHistory());
|
||||
// 判断是否调用了终止工具
|
||||
boolean terminateToolCalled = toolResponseMessage.getResponses().stream()
|
||||
.anyMatch(response -> "doTerminate".equals(response.name()));
|
||||
if (terminateToolCalled) {
|
||||
// 任务结束,更改状态
|
||||
setState(AgentState.FINISHED);
|
||||
}
|
||||
String results = toolResponseMessage.getResponses().stream()
|
||||
.map(response -> "工具 " + response.name() + " 返回的结果:" + response.responseData())
|
||||
.collect(Collectors.joining("\n"));
|
||||
log.info(results);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package com.huangge1199.aiagent.agent.model;
|
||||
|
||||
/**
|
||||
* AgentState
|
||||
*
|
||||
* @author huangge1199
|
||||
* @since 2025/6/4 17:00:54
|
||||
*/
|
||||
/**
|
||||
* 代理执行状态的枚举类
|
||||
*/
|
||||
public enum AgentState {
|
||||
|
||||
/**
|
||||
* 空闲状态
|
||||
*/
|
||||
IDLE,
|
||||
|
||||
/**
|
||||
* 运行中状态
|
||||
*/
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* 已完成状态
|
||||
*/
|
||||
FINISHED,
|
||||
|
||||
/**
|
||||
* 错误状态
|
||||
*/
|
||||
ERROR
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.huangge1199.aiagent.config;
|
||||
|
||||
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.ollama.OllamaChatModel;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -16,16 +17,16 @@ import org.springframework.context.annotation.Primary;
|
||||
public class ChatModelConfig {
|
||||
|
||||
// 选择DashScope作为默认模型
|
||||
// @Bean
|
||||
// @Primary
|
||||
// public ChatModel primaryChatModel(DashScopeChatModel dashscopeChatModel) {
|
||||
// return dashscopeChatModel;
|
||||
// }
|
||||
|
||||
// 或者选择Ollama:
|
||||
@Bean
|
||||
@Primary
|
||||
public ChatModel primaryChatModel(OllamaChatModel ollamaChatModel) {
|
||||
return ollamaChatModel;
|
||||
public ChatModel primaryChatModel(DashScopeChatModel dashscopeChatModel) {
|
||||
return dashscopeChatModel;
|
||||
}
|
||||
|
||||
// // 或者选择Ollama:
|
||||
// @Bean
|
||||
// @Primary
|
||||
// public ChatModel primaryChatModel(OllamaChatModel ollamaChatModel) {
|
||||
// return ollamaChatModel;
|
||||
// }
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.huangge1199.aiagent.controller;
|
||||
|
||||
import com.huangge1199.aiagent.agent.LongManus;
|
||||
import com.huangge1199.aiagent.common.R;
|
||||
import com.huangge1199.aiagent.config.MyLoggerAdvisor;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@ -42,6 +43,9 @@ public class ResController {
|
||||
@Resource
|
||||
private ToolCallbackProvider toolCallbackProvider;
|
||||
|
||||
@Resource
|
||||
private LongManus longManus;
|
||||
|
||||
@PostMapping("/chatRes")
|
||||
@Operation(summary = "返回 ChatResponse")
|
||||
public R<ChatResponse> chatResRes(@RequestBody String question) {
|
||||
@ -108,4 +112,11 @@ public class ResController {
|
||||
String content = chatResponse.getResult().getOutput().getText();
|
||||
return R.ok(content);
|
||||
}
|
||||
|
||||
@PostMapping("/manus")
|
||||
@Operation(summary = "智能体测试")
|
||||
public R<String> manus(@RequestBody String question) {
|
||||
String content = longManus.run(question);
|
||||
return R.ok(content);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ spring:
|
||||
options:
|
||||
model: qwen-plus
|
||||
ollama:
|
||||
base-url: http://192.168.188.2:11435
|
||||
base-url: http://192.168.188.2:11434
|
||||
chat:
|
||||
model: llama3.2:3b
|
||||
vectorstore:
|
||||
|
Loading…
Reference in New Issue
Block a user