从零构建AI智能体:架构设计与代码实现

引言

在上一篇文章中,我们介绍了AI智能体的概念和核心技术。本文将带领读者实际动手构建一个智能体系统,从架构设计到代码实现,全面解析智能体的开发流程。我们将参考OpenManus开源项目的架构,使用Java和Spring AI框架来构建一个具备工具调用能力的智能体。

OpenManus架构解析

OpenManus采用分层代理架构,通过层层继承实现功能扩展。其核心设计遵循"从整体到局部"的组织方式,实现了高度模块化和可扩展性。

核心架构层次

OpenManus通过继承关系构建了清晰的功能层次结构:

  1. BaseAgent:提供基础的状态管理和执行循环
  2. ReActAgent:实现"思考-行动-观察"循环模式
  3. ToolCallAgent:集成工具调用能力
  4. Manus:最终实现的智能体实例,集成多种专用工具

这种分层架构使得开发者可以在不同层次扩展或定制智能体功能,满足不同应用场景的需求。

架构UML类图

下面是OpenManus架构的UML类图,清晰展示了系统各组件的关系:

BaseAgent
-String name
-String systemPrompt
-AgentState state
-int maxSteps
-ChatClient chatClient
-List<Message> messageList
+String run(String userPrompt)
+SseEmitter runStream(String userPrompt)
+abstract String step()
#void cleanup()
ReActAgent
+abstract boolean think()
+abstract String act()
+String step()
ToolCallAgent
-ToolCallback[] availableTools
-ChatResponse toolCallChatResponse
-ToolCallingManager toolCallingManager
+boolean think()
+String act()
Manus
+Manus(ToolCallback[] allTools, ChatModel chatModel)
«enumeration»
AgentState
IDLE
RUNNING
FINISHED
ERROR
ToolResult
-boolean success
-Object result
-String errorMessage
+static ToolResult success(Object result)
+static ToolResult failure(String errorMessage)

组件交互流程

以下是智能体系统各组件的交互流程图:

用户 Manus智能体 大语言模型 工具系统 提交任务 请求思考(think) 返回思考结果和工具选择 调用工具(act) 返回执行结果 更新上下文 直接返回回答 alt [需要执行工具] [无需执行工具] 检查是否完成任务 loop [执行循环] 返回最终结果 用户 Manus智能体 大语言模型 工具系统

构建智能体:步骤详解

步骤一:定义数据模型

首先,我们需要定义智能体的状态模型,用于控制执行流程:

/**
 * 代理执行状态枚举
 */  
public enum AgentState {  
    IDLE,      // 空闲状态
    RUNNING,   // 运行中状态
    FINISHED,  // 已完成状态
    ERROR      // 错误状态
}

步骤二:实现BaseAgent基类

BaseAgent作为所有智能体的基类,负责管理状态和执行流程:

/**
 * 代理基类,管理状态和执行流程
 */
@Data
@Slf4j
public abstract class BaseAgent {

    // 核心属性
    private String name;
    private String systemPrompt;
    private String nextStepPrompt;
    
    // 状态与执行控制
    private AgentState state = AgentState.IDLE;
    private int currentStep = 0;
    private int maxSteps = 10;
    
    // LLM与记忆
    private ChatClient chatClient;
    private List<Message> messageList = new ArrayList<>();

    /**
     * 运行代理
     */
    public String run(String userPrompt) {
        // 校验
        if (state != AgentState.IDLE || StrUtil.isBlank(userPrompt)) {
            throw new RuntimeException("无法执行: " + (state != AgentState.IDLE ? "状态错误" : "空提示词"));
        }
        
        // 执行
        state = AgentState.RUNNING;
        messageList.add(new UserMessage(userPrompt));
        List<String> results = new ArrayList<>();
        
        try {
            // 步骤循环
            for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) {
                currentStep = i + 1;
                log.info("执行步骤 {}/{}", currentStep, maxSteps);
                String stepResult = step();
                results.add("Step " + currentStep + ": " + stepResult);
            }
            
            // 检查终止条件
            if (currentStep >= maxSteps) {
                state = AgentState.FINISHED;
                results.add("终止: 达到最大步骤 (" + maxSteps + ")");
            }
            
            return String.join("\n", results);
        } catch (Exception e) {
            state = AgentState.ERROR;
            log.error("执行错误", e);
            return "执行错误: " + e.getMessage();
        } finally {
            cleanup();
        }
    }
    
    /**
     * 运行代理(流式输出)
     */
    public SseEmitter runStream(String userPrompt) {
        SseEmitter emitter = new SseEmitter(300000L); // 5分钟超时
        
        CompletableFuture.runAsync(() -> {
            try {
                // 校验
                if (state != AgentState.IDLE || StrUtil.isBlank(userPrompt)) {
                    String error = "错误: " + (state != AgentState.IDLE ? "无法从状态运行: " + state : "空提示词");
                    emitter.send(error);
                    emitter.complete();
                    return;
                }
                
                // 执行
                state = AgentState.RUNNING;
                messageList.add(new UserMessage(userPrompt));
                
                // 步骤循环
                for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) {
                    currentStep = i + 1;
                    log.info("执行步骤 {}/{}", currentStep, maxSteps);
                    String stepResult = step();
                    String result = "Step " + currentStep + ": " + stepResult;
                    emitter.send(result);
                }
                
                // 检查终止条件
                if (currentStep >= maxSteps) {
                    state = AgentState.FINISHED;
                    emitter.send("执行结束: 达到最大步骤 (" + maxSteps + ")");
                }
                
                emitter.complete();
            } catch (Exception e) {
                state = AgentState.ERROR;
                log.error("执行错误", e);
                try {
                    emitter.send("执行错误: " + e.getMessage());
                    emitter.complete();
                } catch (IOException ex) {
                    emitter.completeWithError(ex);
                }
            } finally {
                cleanup();
            }
        });

        // 事件处理
        emitter.onTimeout(() -> {
            state = AgentState.ERROR;
            cleanup();
            log.warn("SSE连接超时");
        });
        
        emitter.onCompletion(() -> {
            if (state == AgentState.RUNNING) {
                state = AgentState.FINISHED;
            }
            cleanup();
            log.info("SSE连接完成");
        });
        
        return emitter;
    }

    /**
     * 定义单个执行步骤
     */
    public abstract String step();

    /**
     * 清理资源
     */
    protected void cleanup() {
        // 子类可重写
    }
}

BaseAgent类采用模板方法模式,定义了执行流程的骨架,但将具体的step方法留给子类实现。核心功能包括:

  • 状态管理:通过AgentState枚举控制执行状态
  • 执行循环:实现了step-by-step的迭代执行方式
  • 异常处理:捕获并处理执行过程中的异常
  • 资源清理:提供cleanup方法用于释放资源

步骤三:实现ReActAgent类

ReActAgent继承自BaseAgent,实现了思考-行动循环模式:

/**
 * ReAct模式代理(思考-行动循环)
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public abstract class ReActAgent extends BaseAgent {

    /**
     * 思考决策阶段
     * @return 是否需要执行行动
     */
    public abstract boolean think();

    /**
     * 执行行动阶段
     * @return 行动执行结果
     */
    public abstract String act();

    /**
     * 单步执行思考和行动
     */
    @Override
    public String step() {
        try {
            // 先思考后行动
            boolean shouldAct = think();
            if (!shouldAct) {
                return "思考完成 - 无需行动";
            }
            return act();
        } catch (Exception e) {
            log.error("步骤执行失败", e);
            return "步骤执行失败: " + e.getMessage();
        }
    }
}

ReActAgent类将BaseAgent的step方法分解为think和act两个抽象方法,体现了ReAct模式的核心思想:

  • think:思考决策阶段,返回布尔值决定是否需要执行行动
  • act:行动执行阶段,实际执行操作并返回结果

这种设计使得智能体的推理和行动逻辑分离,便于定制和扩展。

步骤四:实现ToolCallAgent类

ToolCallAgent继承自ReActAgent,具体实现了工具调用能力:

/**
 * 工具调用代理,实现think和act方法处理工具调用
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class ToolCallAgent extends ReActAgent {

    private final ToolCallback[] availableTools;
    private ChatResponse toolCallChatResponse;
    private final ToolCallingManager toolCallingManager;
    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();
    }

    /**
     * 思考阶段:分析当前状态并确定要调用的工具
     */
    @Override
    public boolean think() {
        // 添加下一步提示词
        if (StrUtil.isNotBlank(getNextStepPrompt())) {
            getMessageList().add(new UserMessage(getNextStepPrompt()));
        }
        
        // 调用AI获取工具选择
        try {
            Prompt prompt = new Prompt(getMessageList(), this.chatOptions);
            ChatResponse chatResponse = getChatClient().prompt(prompt)
                    .system(getSystemPrompt())
                    .tools(availableTools)
                    .call()
                    .chatResponse();
                    
            this.toolCallChatResponse = chatResponse;
            
            // 解析响应
            AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
            List<AssistantMessage.ToolCall> toolCallList = assistantMessage.getToolCalls();
            
            // 记录选择
            String result = assistantMessage.getText();
            log.info("{}的思考: {}", getName(), result);
            log.info("{}选择了{}个工具", getName(), toolCallList.size());
            
            if (toolCallList.size() > 0) {
                String toolCallInfo = toolCallList.stream()
                    .map(tc -> String.format("工具: %s, 参数: %s", tc.name(), tc.arguments()))
                    .collect(Collectors.joining("\n"));
                log.info(toolCallInfo);
            }
            
            // 没有工具调用时记录助手消息
            if (toolCallList.isEmpty()) {
                getMessageList().add(assistantMessage);
                return false;
            }
            
            return true;
        } catch (Exception e) {
            log.error("{}思考过程错误: {}", getName(), e.getMessage());
            getMessageList().add(new AssistantMessage("处理错误: " + e.getMessage()));
            return false;
        }
    }

    /**
     * 行动阶段:执行选定的工具
     */
    @Override
    public String act() {
        if (!toolCallChatResponse.hasToolCalls()) {
            return "没有工具需要调用";
        }
        
        // 执行工具调用
        Prompt prompt = new Prompt(getMessageList(), this.chatOptions);
        ToolExecutionResult result = toolCallingManager.executeToolCalls(prompt, toolCallChatResponse);
        
        // 更新消息上下文
        setMessageList(result.conversationHistory());
        ToolResponseMessage response = (ToolResponseMessage) CollUtil.getLast(result.conversationHistory());
        
        // 检查是否调用了终止工具
        boolean terminated = response.getResponses().stream()
                .anyMatch(r -> r.name().equals("doTerminate"));
        if (terminated) {
            setState(AgentState.FINISHED);
        }
        
        // 格式化结果
        String results = response.getResponses().stream()
                .map(r -> "工具[" + r.name() + "]结果: " + r.responseData())
                .collect(Collectors.joining("\n"));
        log.info(results);
        
        return results;
    }
}

ToolCallAgent类实现了think和act方法,是智能体与工具系统交互的核心:

  • think方法:使用LLM决定调用哪些工具
  • act方法:执行工具调用并处理结果
  • 工具管理:管理可用工具集合,实现工具选择和执行

下面是工具调用流程的示意图:

接收用户输入
添加下一步提示词
调用LLM请求工具选择
是否需要调用工具?
执行工具调用
更新消息上下文
是否调用了终止工具?
设置状态为已完成
格式化结果
添加助手消息到上下文
返回无需行动结果
返回工具执行结果

步骤五:实现最终智能体LenManus

LenManus继承自ToolCallAgent,是实际可用的智能体实例:

/**
 * 超级智能体,具备自主规划能力
 */
@Component
public class LenManus extends ToolCallAgent {

    public LenManus(ToolCallback[] allTools, ChatModel dashscopeChatModel) {
        super(allTools);
        
        // 基础配置
        this.setName("lenManus");
        this.setMaxSteps(20);
        
        // 提示词设置
        this.setSystemPrompt(
            "You are LenManus, 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.setNextStepPrompt(
            "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.setChatClient(
            ChatClient.builder(dashscopeChatModel)
                .defaultAdvisors(new MyLoggerAdvisor())
                .build()
        );
    }
}

LenManus类主要配置了智能体的属性和行为:

  • 系统提示词:定义智能体的角色和能力
  • 下一步提示词:引导智能体在每个步骤的决策
  • 最大步数:控制执行的最大迭代次数
  • 对话客户端:设置与LLM交互的客户端

步骤六:开发工具系统

工具系统是智能体能力扩展的关键。下面是一个终止工具的实现示例:

/**
 * 终止工具 - 允许智能体自主结束任务
 */
@Slf4j
public class TerminateTool {

    /**
     * 终止当前任务
     */
    @Tool(description = "Use this tool to terminate the current task when you believe it is complete or cannot proceed further", returnDirect = false)
    public String doTerminate(
            @ToolParam(description = "Reason for terminating the task") String reason) {
        log.info("任务终止: {}", reason);
        return "任务已终止: " + reason;
    }
}

一个文件操作工具示例:

/**
 * 文件操作工具
 */
@Slf4j
public class FileOperationTool {

    /**
     * 读取文件内容
     */
    @Tool(description = "Read content from a file", returnDirect = false)
    public String readFile(
            @ToolParam(description = "Path to the file to read") String filePath) {
        try {
            File file = new File(filePath);
            if (!file.exists()) {
                return "错误: 文件不存在";
            }
            
            if (!file.isFile() || !file.canRead()) {
                return "错误: 无法读取文件";
            }
            
            String content = FileUtil.readString(file, StandardCharsets.UTF_8);
            return "文件内容:\n" + content;
        } catch (Exception e) {
            log.error("读取文件失败", e);
            return "读取文件失败: " + e.getMessage();
        }
    }
    
    /**
     * 写入文件内容
     */
    @Tool(description = "Write content to a file", returnDirect = false)
    public String writeFile(
            @ToolParam(description = "Path to the file to write") String filePath,
            @ToolParam(description = "Content to write to the file") String content) {
        try {
            File file = new File(filePath);
            File parent = file.getParentFile();
            
            if (parent != null && !parent.exists()) {
                if (!parent.mkdirs()) {
                    return "错误: 无法创建目录";
                }
            }
            
            FileUtil.writeString(content, file, StandardCharsets.UTF_8);
            return "文件已成功写入: " + filePath;
        } catch (Exception e) {
            log.error("写入文件失败", e);
            return "写入文件失败: " + e.getMessage();
        }
    }
}

智能体循环检测与优化

为了避免智能体陷入循环,我们可以实现循环检测和处理机制:

/**
 * 检查代理是否陷入循环
 */
protected boolean isStuck() {
    if (messageList.size() < 2) {
        return false;
    }
    
    // 获取最后一条助手消息
    AssistantMessage lastAssistantMessage = null;
    for (int i = messageList.size() - 1; i >= 0; i--) {
        if (messageList.get(i) instanceof AssistantMessage) {
            lastAssistantMessage = (AssistantMessage) messageList.get(i);
            break;
        }
    }
    
    if (lastAssistantMessage == null || lastAssistantMessage.getText() == null 
            || lastAssistantMessage.getText().isEmpty()) {
        return false;
    }
    
    // 计算重复内容出现次数
    int duplicateCount = 0;
    String lastContent = lastAssistantMessage.getText();
    
    for (int i = messageList.size() - 2; i >= 0; i--) {
        Message msg = messageList.get(i);
        if (msg instanceof AssistantMessage) {
            AssistantMessage assistantMsg = (AssistantMessage) msg;
            if (lastContent.equals(assistantMsg.getText())) {
                duplicateCount++;
                
                if (duplicateCount >= this.duplicateThreshold) {
                    return true;
                }
            }
        }
    }
    
    return false;
}

/**
 * 处理陷入循环的状态
 */
protected void handleStuckState() {
    String stuckPrompt = "观察到重复响应。请考虑新的策略,避免重复已尝试过的无效路径。";
    this.nextStepPrompt = stuckPrompt + "\n" + (this.nextStepPrompt != null ? this.nextStepPrompt : "");
    log.warn("检测到智能体陷入循环状态。添加额外提示: {}", stuckPrompt);
}

循环检测机制主要通过比较历史消息来识别重复响应,当检测到循环时,通过添加特殊提示词引导智能体跳出循环。

循环检测与处理流程图

<2
>=2
继续
结束
检查消息列表大小
返回false
查找最后一条助手消息
是否找到有效消息?
遍历历史消息
是否与最后消息相同?
继续遍历
增加重复计数
重复次数>=阈值?
返回true
处理循环状态
生成特殊提示
添加到下一步提示词
记录警告日志

测试智能体

创建单元测试验证智能体功能:

@SpringBootTest
class LenManusTest {

    @Resource
    private LenManus lenManus;

    @Test
    public void run() {
        String userPrompt = """
                我的另一半居住在广东广州,请帮我找到 5 公里内合适的约会地点,
                并结合一些网络图片,制定一份详细的约会计划,
                并以 PDF 格式输出""";
        String answer = lenManus.run(userPrompt);
        Assertions.assertNotNull(answer);
    }
}

测试示例展示了如何使用智能体解决复杂任务,包括地点搜索、图片查找和PDF生成。

扩展功能:PDF生成能力

为智能体添加PDF生成工具,扩展其能力边界:

/**
 * PDF生成工具
 */
public class PDFGenerationTool {

    // Markdown标题正则
    private static final Pattern HEADER_PATTERN = Pattern.compile("^(#{1,6})\\s+(.+)$");
    
    // Markdown图片正则
    private static final Pattern IMAGE_PATTERN = Pattern.compile("!\\[(.*?)\\]\\((.*?)\\)");

    /**
     * 生成PDF文件
     */
    @Tool(description = "Generate a PDF file with given content and images, supports markdown image syntax ![](url) and headers with # symbols", returnDirect = false)
    public String generatePDF(
            @ToolParam(description = "Name of the file to save the generated PDF") String fileName,
            @ToolParam(description = "Content to be included in the PDF, supports markdown image syntax ![](url) and headers with # symbols") String content) {
        String fileDir = FileConstant.FILE_SAVE_DIR + "/pdf";
        String filePath = fileDir + "/" + fileName;
        
        try {
            FileUtil.mkdir(fileDir);
            
            try (PdfWriter writer = new PdfWriter(filePath);
                 PdfDocument pdf = new PdfDocument(writer);
                 Document document = new Document(pdf)) {
                
                PdfFont font = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");
                document.setFont(font);
                
                processContent(content, document, font);
                
            }
            return "PDF生成成功,保存路径: " + filePath;
        } catch (IOException e) {
            return "生成PDF时出错: " + e.getMessage();
        }
    }
    
    /**
     * 处理内容
     */
    private void processContent(String content, Document document, PdfFont font) {
        String[] lines = content.split("\n");
        StringBuilder textBuffer = new StringBuilder();
        
        for (String line : lines) {
            Matcher headerMatcher = HEADER_PATTERN.matcher(line);
            if (headerMatcher.matches()) {
                if (textBuffer.length() > 0) {
                    processTextWithImages(textBuffer.toString(), document);
                    textBuffer.setLength(0);
                }
                
                String headerMarker = headerMatcher.group(1);
                String headerText = headerMatcher.group(2);
                addHeader(document, headerText, headerMarker.length(), font);
                continue;
            }
            
            textBuffer.append(line).append("\n");
        }
        
        if (textBuffer.length() > 0) {
            processTextWithImages(textBuffer.toString(), document);
        }
    }
    
    /**
     * 添加标题
     */
    private void addHeader(Document document, String headerText, int level, PdfFont font) {
        Paragraph header = new Paragraph(headerText);
        
        float fontSize = 24f;
        switch (level) {
            case 1: fontSize = 24f; break;
            case 2: fontSize = 20f; break;
            case 3: fontSize = 18f; break;
            case 4: fontSize = 16f; break;
            case 5: fontSize = 14f; break;
            case 6: fontSize = 12f; break;
        }
        
        header.setFont(font)
              .setFontSize(fontSize)
              .setBold()
              .setTextAlignment(TextAlignment.LEFT);
              
        // 添加适当的间距
        header.setMarginTop(20f);
        header.setMarginBottom(10f);
        
        document.add(header);
    }
    
    /**
     * 处理包含图片的文本
     */
    private void processTextWithImages(String content, Document document) {
        Matcher matcher = IMAGE_PATTERN.matcher(content);
        
        int lastEnd = 0;
        
        while (matcher.find()) {
            String textBefore = content.substring(lastEnd, matcher.start());
            if (!textBefore.isEmpty()) {
                document.add(new Paragraph(textBefore));
            }
            
            String imageUrl = matcher.group(2);
            try {
                // 使用iText的ImageDataFactory加载远程图像
                Image image = new Image(ImageDataFactory.create(new URL(imageUrl)));
                
                // 设置图片宽度和自动缩放
                image.setWidth(document.getPdfDocument().getDefaultPageSize().getWidth() * 0.8f);
                image.setAutoScale(true);
                
                // 添加图片标题(如果有)
                String imageCaption = matcher.group(1);
                if (StrUtil.isNotBlank(imageCaption)) {
                    // 创建包含图片和标题的垂直布局
                    Div imageContainer = new Div();
                    imageContainer.setTextAlignment(TextAlignment.CENTER);
                    imageContainer.add(image);
                    
                    // 添加图片标题
                    Paragraph caption = new Paragraph(imageCaption)
                            .setFontSize(10f)
                            .setItalic()
                            .setTextAlignment(TextAlignment.CENTER);
                    imageContainer.add(caption);
                    
                    document.add(imageContainer);
                } else {
                    // 直接添加图片
                    document.add(image);
                }
            } catch (Exception e) {
                document.add(new Paragraph("无法加载图片: " + imageUrl + " (" + e.getMessage() + ")"));
            }
            
            lastEnd = matcher.end();
        }
        
        if (lastEnd < content.length()) {
            document.add(new Paragraph(content.substring(lastEnd)));
        }
    }
}

PDF生成工具支持Markdown语法,能够处理文本格式和图片嵌入,为智能体提供了输出结构化文档的能力。下面是PDF生成的流程图:

接收内容与文件名
创建PDF文档
处理内容文本
是标题行?
添加标题
添加到文本缓冲区
处理缓冲区文本
包含图片标记?
提取图片URL
加载远程图片
添加图片到文档
添加纯文本段落
保存PDF文件
返回成功结果

项目文件结构示例

一个完整的智能体项目通常包含以下文件结构:

src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── example/
│   │           └── agent/
│   │               ├── Application.java             // 应用程序入口
│   │               ├── config/
│   │               │   └── AgentConfiguration.java  // 智能体配置
│   │               ├── controller/
│   │               │   └── AgentController.java     // API控制器
│   │               ├── model/
│   │               │   ├── AgentState.java          // 状态枚举
│   │               │   ├── Message.java             // 消息模型
│   │               │   └── ToolResult.java          // 工具结果模型
│   │               ├── agent/
│   │               │   ├── BaseAgent.java           // 基础智能体
│   │               │   ├── ReActAgent.java          // ReAct智能体
│   │               │   ├── ToolCallAgent.java       // 工具调用智能体
│   │               │   └── LenManus.java            // 最终智能体实现
│   │               ├── tools/
│   │               │   ├── FileOperationTool.java   // 文件操作工具
│   │               │   ├── PDFGenerationTool.java   // PDF生成工具
│   │               │   ├── TerminateTool.java       // 终止工具
│   │               │   ├── WebSearchTool.java       // 网络搜索工具
│   │               │   └── ImageSearchTool.java     // 图片搜索工具
│   │               └── utils/
│   │                   ├── FileUtil.java            // 文件工具类
│   │                   └── PromptUtil.java          // 提示词工具类
│   └── resources/
│       ├── application.yml                          // 应用配置
│       └── static/                                  // 静态资源
└── test/
    └── java/
        └── com/
            └── example/
                └── agent/
                    └── LenManusTest.java            // 智能体测试类

结语

通过本文的实践,我们完成了一个具备工具调用能力的AI智能体系统,从架构设计到代码实现全面解析了开发流程。这种模块化、分层的架构设计使得智能体系统具有高度的灵活性和可扩展性,能够适应各种应用场景。

在实际应用中,开发者可以根据需求进一步扩展智能体的功能,例如:

  1. 知识库集成:连接向量数据库,实现专业领域知识支持
  2. 多模态交互:支持图像、音频等多模态输入和输出
  3. 持久化记忆:实现长期记忆存储,保持上下文连贯性
  4. 自适应提示词:根据任务复杂度动态调整提示词策略

在下一篇文章中,我们将介绍如何进一步扩展智能体的功能,实现更复杂的业务场景,例如专业领域智能体和多智能体协作系统。


参考资料:

  1. OpenManus开源项目:https://github.com/FoundationAgents/OpenManus
  2. Spring AI文档:https://docs.spring.io/spring-ai/reference/
  3. iText PDF库:https://itextpdf.com/
  4. 智能体设计模式:https://martinfowler.com/articles/patterns-of-distributed-systems/

作者:lenyan
GitHub:lenyanjgk · GitHub
CSDN:lenyan~-CSDN博客

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐