智能体完成
This commit is contained in:
parent
ba744a5f3d
commit
001448fb5b
9
pom.xml
9
pom.xml
@ -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>
|
||||||
|
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;
|
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;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user