智能体完成

This commit is contained in:
huangge1199 2025-06-10 14:32:43 +08:00
parent ba744a5f3d
commit 001448fb5b
9 changed files with 403 additions and 21 deletions

View File

@ -20,15 +20,6 @@
<developers> <developers>
<developer/> <developer/>
</developers> </developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<modules>
<module>long-image-search-mcp-server</module>
</modules>
<properties> <properties>
<java.version>21</java.version> <java.version>21</java.version>
</properties> </properties>

View 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() {
// 子类可以重写此方法来清理资源
}
}

View 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);
}
}

View 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();
}
}
}

View 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;
}
}

View File

@ -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
}

View File

@ -1,5 +1,6 @@
package com.huangge1199.aiagent.config; package com.huangge1199.aiagent.config;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -16,16 +17,16 @@ import org.springframework.context.annotation.Primary;
public class ChatModelConfig { public class ChatModelConfig {
// 选择DashScope作为默认模型 // 选择DashScope作为默认模型
// @Bean @Bean
// @Primary @Primary
// public ChatModel primaryChatModel(DashScopeChatModel dashscopeChatModel) { public ChatModel primaryChatModel(DashScopeChatModel dashscopeChatModel) {
// return dashscopeChatModel; return dashscopeChatModel;
// } }
// 或者选择Ollama // // 或者选择Ollama
@Bean // @Bean
@Primary // @Primary
public ChatModel primaryChatModel(OllamaChatModel ollamaChatModel) { // public ChatModel primaryChatModel(OllamaChatModel ollamaChatModel) {
return ollamaChatModel; // return ollamaChatModel;
} // }
} }

View File

@ -1,5 +1,6 @@
package com.huangge1199.aiagent.controller; package com.huangge1199.aiagent.controller;
import com.huangge1199.aiagent.agent.LongManus;
import com.huangge1199.aiagent.common.R; import com.huangge1199.aiagent.common.R;
import com.huangge1199.aiagent.config.MyLoggerAdvisor; import com.huangge1199.aiagent.config.MyLoggerAdvisor;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -42,6 +43,9 @@ public class ResController {
@Resource @Resource
private ToolCallbackProvider toolCallbackProvider; private ToolCallbackProvider toolCallbackProvider;
@Resource
private LongManus longManus;
@PostMapping("/chatRes") @PostMapping("/chatRes")
@Operation(summary = "返回 ChatResponse") @Operation(summary = "返回 ChatResponse")
public R<ChatResponse> chatResRes(@RequestBody String question) { public R<ChatResponse> chatResRes(@RequestBody String question) {
@ -108,4 +112,11 @@ public class ResController {
String content = chatResponse.getResult().getOutput().getText(); String content = chatResponse.getResult().getOutput().getText();
return R.ok(content); return R.ok(content);
} }
@PostMapping("/manus")
@Operation(summary = "智能体测试")
public R<String> manus(@RequestBody String question) {
String content = longManus.run(question);
return R.ok(content);
}
} }

View File

@ -9,7 +9,7 @@ spring:
options: options:
model: qwen-plus model: qwen-plus
ollama: ollama:
base-url: http://192.168.188.2:11435 base-url: http://192.168.188.2:11434
chat: chat:
model: llama3.2:3b model: llama3.2:3b
vectorstore: vectorstore: