大模型开发 - 用纯Java手写一个多功能AI Agent:01 从零实现类Manus智能体

大模型开发 - 用纯Java手写一个多功能AI Agent:01 从零实现类Manus智能体

文章目录

在这里插入图片描述

引言

2024年以来,AI Agent(智能体)成为大模型应用领域最炙手可热的方向。从OpenAI的GPT-4 with Tools,到Anthropic的Claude Computer Use,再到国内Manus、AutoGLM等产品,"让大模型不只是聊天,而是真正地做事"已经成为行业共识。

然而,大多数开发者对AI Agent的理解还停留在概念层面:知道它能调用工具、能自主决策,但对其内部运作机制缺乏深入了解。市面上的Agent框架(如LangChain、Spring AI)虽然降低了开发门槛,但也隐藏了大量实现细节。

本文将通过一个完全不依赖Spring框架的纯Java项目——ai-manus,带你从零理解AI Agent的核心架构。这个项目实现了一个功能完整的多工具智能体,具备文件读写、Docker沙箱代码执行、网页搜索、浏览器自动化等能力,并采用了ReAct(Reasoning + Acting)推理模式和LLM驱动的上下文记忆管理。通过逐层拆解其代码实现,你将真正理解一个AI Agent是如何"思考"和"行动"的。

一、项目全景:架构与技术选型

1.1 项目结构

ai-manus/ ├── pom.xml └── src/main/java/com/artisan/ ├── ManusApplication.java # 启动入口 ├── agent/ │ ├── BaseAgent.java # Agent基类(循环控制) │ ├── ToolCallAgent.java # 工具调用Agent(ReAct核心) │ └── ManusAgent.java # 具体Agent实现(注册工具) ├── model/ │ ├── ModelConfig.java # 模型配置 │ ├── OpenAIClient.java # LLM API客户端 │ ├── Message.java # 消息模型(支持多模态) │ ├── Memory.java # 记忆管理 │ ├── RelevanceFilter.java # 相关性过滤接口 │ ├── LLMRelevanceFilter.java # 基于LLM的相关性过滤 │ ├── ModelResponse.java # 模型响应 │ ├── Role.java # 角色枚举 │ ├── ToolCall.java # 工具调用模型 │ ├── ToolDefinition.java # 工具定义模型 │ └── Function.java # 函数调用模型 └── tools/ ├── Tool.java # 工具接口 ├── BaseTool.java # 工具基类 ├── ToolCollection.java # 工具注册中心 ├── ToolResult.java # 工具执行结果 └── impl/ ├── FileWriterTool.java # 文件写入 ├── FileReaderTool.java # 文件读取 ├── SandboxTool.java # Docker沙箱执行 ├── DockerSandbox.java # Docker容器管理 ├── TavilySearchTool.java # 网页搜索 └── BrowserTool.java # 浏览器自动化 

1.2 技术选型

项目刻意避开了Spring等重型框架,采用"最小依赖"原则:

依赖版本用途
OkHttp34.12.0HTTP客户端,调用LLM API
Jackson2.16.1JSON序列化/反序列化
Lombok1.18.30减少样板代码
docker-java3.3.6Docker容器管理
langchain4j-tavily0.36.2Tavily网页搜索
Playwright1.55.0浏览器自动化

这种选择的好处是:你看到的每一行代码都是Agent逻辑本身,没有框架魔法的干扰,非常适合学习和理解Agent的运作原理。

二、Agent核心循环:ReAct模式的实现

ReAct(Reasoning + Acting)是当前AI Agent最主流的推理范式。其核心思想是:大模型在每一步先进行推理(Thought),然后决定执行什么动作(Action),观察执行结果(Observation),再进入下一轮推理。这个项目通过三层Agent继承体系优雅地实现了这一模式。

2.1 BaseAgent:循环骨架

BaseAgent是所有Agent的抽象基类,它定义了Agent执行的基本骨架——一个有限步数的循环:

publicabstractclassBaseAgent{protectedfinalMemory memory;privatefinalint maxStep;protectedString systemPrompt;publicStringrun(String prompt){// 1. 初始化:将系统提示词和用户输入加入记忆 memory.addMessage(Message.systemMessage(systemPrompt)); memory.addMessage(Message.userMessage(prompt));int currentStep =0;StringBuilder allStepResult =newStringBuilder();// 2. 核心循环:最多执行maxStep步while(currentStep < maxStep){StepResult stepResult =step(prompt);// 子类实现 allStepResult.append(stepResult.output).append("/n");if(!stepResult.isShouldContinue()){break;// Agent认为任务完成,退出循环} currentStep++;}return allStepResult.toString();}// 由子类实现的单步执行逻辑protectedabstractStepResultstep(String currentQuery);}

这里有几个关键设计决策:

  • 最大步数限制(maxStep=10):防止Agent陷入无限循环,这是所有Agent系统必备的安全阀。
  • StepResult双字段设计output记录当前步的输出,shouldContinue标识是否需要继续执行。大模型通过返回finish_reason="stop"来告知Agent任务已完成。
  • Memory贯穿全程:所有消息(系统提示、用户输入、助手回复、工具结果)都存入Memory,保证上下文连贯性。

2.2 ToolCallAgent:ReAct的核心引擎

ToolCallAgent是整个系统的灵魂所在,它实现了ReAct循环的单步逻辑:

@OverrideprotectedStepResultstep(String currentQuery){// 1. 从记忆中获取上下文消息(带相关性过滤)List<Message> contextMessages = memory.getMessages(currentQuery);// 2. 获取与当前查询相关的工具定义(带相关性过滤)List<ToolDefinition> toolDefinitions = toolCollection.getRelevantToolDefinitions(currentQuery);// 3. 调用大模型,传入上下文消息和可用工具ModelResponse modelResponse = openAIClient.chat(contextMessages, toolDefinitions);// 4. 大模型决定调用工具if(modelResponse.hasToolCalls()){// 将工具调用请求存入记忆Message assistantMessage =Message.assistantMessage(modelResponse.getContent()); assistantMessage.setToolCalls(convertToToolCalls(modelResponse.getToolCalls())); memory.addMessage(assistantMessage);// 执行工具并返回结果returnhandleToolCalls(modelResponse.getToolCalls());}// 5. 大模型不调用工具,直接返回文本if(modelResponse.getContent()!=null&&!modelResponse.getContent().isBlank()){ memory.addMessage(Message.assistantMessage(modelResponse.getContent()));}// 6. 判断是否结束if(modelResponse.getFinishReason().equals("stop")){returnStepResult.builder().shouldContinue(false).output("大模型认为任务已经执行结束").build();}returnStepResult.builder().shouldContinue(true).output(modelResponse.getContent()).build();}

让我们拆解这个方法中蕴含的设计智慧:

上下文管理:每次调用LLM前,先通过memory.getMessages(currentQuery)获取经过相关性过滤的消息,避免上下文窗口溢出。

工具动态筛选:不是每次都把所有5个工具都传给大模型,而是通过getRelevantToolDefinitions根据当前查询动态选择最相关的工具,减少干扰提升决策质量。

消息链维护:严格遵循OpenAI Tool Calling协议——先将assistant的tool_calls消息存入记忆,再存入对应的tool执行结果消息。这个消息链的完整性是大模型正确理解上下文的关键。

工具执行的核心逻辑在handleToolCalls方法中:

privateStepResulthandleToolCalls(List<Object> toolCalls){StringBuilder allResults =newStringBuilder();for(Object toolCallObj : toolCalls){try{JsonNode toolCallNode = objectMapper.valueToTree(toolCallObj);String toolCallId = toolCallNode.get("id").asText();String toolName = toolCallNode.get("function").get("name").asText();String argumentsJson = toolCallNode.get("function").get("arguments").asText();// 解析参数并执行工具Map<String,Object> arguments = objectMapper.readValue(argumentsJson,Map.class);ToolResult result = toolCollection.executeTool(toolName, arguments);// 将工具结果封装为toolMessage存入记忆String resultContent = result.hasError()?"Error: "+ result.getError(): result.getOutput().toString();Message toolMessage =Message.toolMessage( resultContent, toolName, toolCallId, result.getBase64Image()); memory.addMessage(toolMessage);}catch(Exception e){// 错误也要存入记忆,让大模型知道发生了什么Message errorMessage =Message.toolMessage("工具执行失败: "+ e.getMessage(),"unknown", UUID.randomUUID().toString()); memory.addMessage(errorMessage);}}returnStepResult.builder().shouldContinue(true).output(allResults.toString()).build();}

注意这里的一个细节:工具执行完成后,shouldContinue始终为true。这意味着工具执行只是Agent的一个中间步骤,执行完工具后需要将结果反馈给大模型,由大模型决定下一步做什么——可能继续调用其他工具,也可能认为任务已完成。这就是ReAct循环的精髓。

2.3 ManusAgent:具体Agent的组装

ManusAgent是最终面向用户的Agent实现,负责注册工具和配置系统提示词:

publicclassManusAgentextendsToolCallAgent{privatefinalstaticString SYSTEM_PROMPT =""" # 角色定义 你是Manus,一个多功能的AI代理,能够使用可用的工具处理各种任务。 # 规则 - 工作目录:{workspace} - Sandbox里面不使用工作目录 - 利用Sandbox执行代码时,直接把代码内容传给Sandbox,而不是把代码脚本文件传给Sandbox - 一次只能执行一个工具 """;publicManusAgent(OpenAIClient openAIClient){super(openAIClient,null,null);// 注册5个工具ToolCollection toolCollection =newToolCollection(); toolCollection.addTool(newFileWriterTool()); toolCollection.addTool(newFileReaderTool()); toolCollection.addTool(newSandboxTool()); toolCollection.addTool(newTavilySearchTool()); toolCollection.addTool(newBrowserTool());this.toolCollection = toolCollection;// 创建工作区目录并注入到系统提示词Path workspaceRoot =getProjectRoot().resolve("workspace");Files.createDirectories(workspaceRoot);this.systemPrompt = SYSTEM_PROMPT.replace("{workspace}", workspaceRoot.toString());}}

系统提示词的设计值得关注:它明确了Agent的角色定位、工作目录规范以及工具使用规则。特别是"一次只能执行一个工具"这条规则,简化了工具执行的并发复杂度。

三、消息系统:多模态对话的基石

3.1 四种角色的消息设计

publicenumRole{SYSTEM("system"),// 系统提示词USER("user"),// 用户输入ASSISTANT("assistant"),// 大模型回复(含tool_calls)TOOL("tool");// 工具执行结果}

Message类是整个消息系统的核心,它需要兼容OpenAI Chat Completions API的消息格式:

publicclassMessage{privateRole role;privateString content;privateList<ToolCall> toolCalls;// assistant消息携带的工具调用privateString name;// tool消息的工具名privateString toolCallId;// tool消息关联的调用IDprivateString base64Image;// 多模态图片数据}

这里有一个精妙的设计——base64Image字段使得消息天然支持多模态。当浏览器工具截图后,截图数据以Base64编码附在toolMessage中传回;大模型在下一轮接收到这个多模态消息时,就能"看到"截图内容并进行分析。

OpenAIClient中,多模态消息的转换逻辑如下:

if(message.getBase64Image()!=null){// 构造多模态content数组List<Map<String,Object>> content =newArrayList<>(); content.add(Map.of("type","text","text", message.getContent())); content.add(Map.of("type","image_url","image_url",Map.of("url","data:image/jpeg;base64,"+ message.getBase64Image()))); apiMessage.put("content", content);}else{ apiMessage.put("content", message.getContent());}

3.2 LLM API的封装

OpenAIClient使用OkHttp3直接调用OpenAI兼容API(本项目实际对接的是阿里云DashScope的通义千问模型):

publicclassOpenAIClient{privatefinalOkHttpClient httpClient;publicOpenAIClient(ModelConfig modelConfig){this.httpClient =newOkHttpClient.Builder().connectTimeout(Duration.ofSeconds(30)).readTimeout(Duration.ofMinutes(5))// 长超时,等待大模型推理.writeTimeout(Duration.ofMinutes(5)).build();}publicModelResponsechat(List<Message> messages,List<ToolDefinition> tools){Map<String,Object> requestBody =newHashMap<>(); requestBody.put("model", modelConfig.getModel()); requestBody.put("messages",convertMessagesToApiFormat(messages));// 仅在有工具时传入tools参数if(tools !=null&&!tools.isEmpty()){ requestBody.put("tools",convertToolsToApiFormat(tools));}// ... 发送请求并解析响应}}

工具定义的转换遵循OpenAI Function Calling标准格式:

privateList<Map<String,Object>>convertToolsToApiFormat(List<ToolDefinition> tools){return tools.stream().map(tool ->Map.of("type","function","function",Map.of("name", tool.getName(),"description", tool.getDescription(),"parameters", tool.getParameters()// JSON Schema格式))).toList();}

四、记忆管理:LLM驱动的上下文过滤

随着Agent执行步骤增多,消息历史会快速膨胀。如果不加处理,很快就会超出大模型的上下文窗口。本项目提供了一个创新的解决方案——用LLM自身来评估消息的相关性

4.1 相关性过滤接口

publicinterfaceRelevanceFilter{List<Message>filter(List<Message> messages,String currentQuery,int maxMessages);doublecalculateRelevance(Message message,String currentQuery);}

Memory类在获取消息时会自动应用过滤:

publicList<Message>getMessages(String currentQuery){if(relevanceFilter !=null){return relevanceFilter.filter(messages, currentQuery,5);// 最多保留5条}return messages;}

4.2 LLM相关性过滤器

LLMRelevanceFilter是这个机制的核心实现。它对每条非系统消息调用LLM进行语义相关性评分:

publicList<Message>filter(List<Message> messages,String currentQuery,int maxMessages){// 系统消息始终保留List<Message> systemMessages = messages.stream().filter(msg -> msg.getRole()==Role.SYSTEM).toList();// 为每条非系统消息计算相关性得分List<MessageScore> scoredMessages = nonSystemMessages.stream().map(msg ->newMessageScore(msg,calculateRelevance(msg, currentQuery))).sorted((a, b)->Double.compare(b.score, a.score)).toList();// 保留系统消息 + 得分最高的N条消息List<Message> result =newArrayList<>(systemMessages);int remainingSlots = maxMessages - systemMessages.size(); result.addAll(scoredMessages.stream().limit(remainingSlots).map(ms -> ms.message).toList());return result;}

评分时构造的提示词非常讲究,提供了明确的评分标准:

privateStringbuildRelevancePrompt(String messageContent,String query){returnString.format("请评估以下消息内容与查询的相关性,返回0.0到1.0之间的数字评分:\n\n"+"查询:%s\n\n消息内容:%s\n\n"+"评估标准:\n"+"1.0 - 高度相关:消息直接回答查询或包含查询的核心信息\n"+"0.7-0.9 - 相关:消息与查询主题相关,包含有用信息\n"+"0.4-0.6 - 部分相关:消息与查询有一定关联\n"+"0.1-0.3 - 微弱相关:消息与查询只有很少关联\n"+"0.0 - 不相关:消息与查询完全无关\n\n"+"请只返回数字评分,不要包含其他文字说明:", query, messageContent );}

同时,代码还实现了健壮的评分解析逻辑,能处理LLM返回数字或文本描述两种情况:

privatedoubleparseRelevanceScore(String content){// 优先尝试解析数字String numberStr = trimmed.replaceAll("[^0-9.].*","");if(!numberStr.isEmpty()){returnDouble.parseDouble(numberStr);}// 回退:从文本关键词推断if(lowerContent.contains("高度相关"))return0.9;if(lowerContent.contains("相关"))return0.7;if(lowerContent.contains("部分"))return0.5;if(lowerContent.contains("微弱"))return0.2;if(lowerContent.contains("不相关"))return0.0;return0.0;}

4.3 工具的动态过滤

同样的相关性过滤机制也应用于工具选择。ToolCollection会将每个工具的"名称+描述"包装成消息,让LLM评估其与当前查询的相关性:

publicList<ToolDefinition>getRelevantToolDefinitions(String query){List<ToolScore> scoredTools = allTools.stream().map(tool ->{String toolText = tool.getName()+" "+ tool.getDescription();Message message =Message.assistantMessage(toolText);double relevance = relevanceFilter.calculateRelevance(message, query);returnnewToolScore(tool, relevance);}).filter(ts -> ts.score >=0.3)// 相关性阈值.sorted((a, b)->Double.compare(b.score, a.score)).toList();// 安全兜底:如果没有工具超过阈值,返回全部if(relevantTools.isEmpty()){return allTools;}return relevantTools;}

这个设计有一个重要的安全阈值——0.3。当没有任何工具的相关性超过0.3时,系统会回退到返回所有工具,避免Agent"无工具可用"的死锁状态。

五、工具系统:可插拔的能力扩展

5.1 工具接口与基类

工具系统采用经典的接口-抽象类-实现类三层设计:

publicinterfaceTool{StringgetName();StringgetDescription();Map<String,Object>getParametersSchema();// JSON SchemaToolResultexecute(Map<String,Object> parameters);defaultToolDefinitiontoDefinition(){returnnewToolDefinition(getName(),getDescription(),getParametersSchema());}}

BaseTool提供了构建JSON Schema参数定义的辅助方法:

publicabstractclassBaseToolimplementsTool{// 参数Schema构建器protectedMap<String,Object>stringParam(String description){...}protectedMap<String,Object>boolParam(String description){...}protectedMap<String,Object>intParam(String description){...}protectedMap<String,Object>enumParam(String description,List<String> values){...}// 参数安全提取protectedStringgetString(Map<String,Object> parameters,String key){...}protectedBooleangetBoolean(Map<String,Object> parameters,String key){...}protectedIntegergetInteger(Map<String,Object> parameters,String key){...}// Schema组装protectedMap<String,Object>buildSchema(Map<String,Map<String,Object>> properties,List<String> required){...}}

ToolResult支持三种返回形态——纯文本、错误信息、带图片的多模态结果:

publicclassToolResult{privatefinalObject output;privatefinalString error;privatefinalString base64Image;// 支持截图等多模态输出publicstaticToolResultsuccess(Object output){...}publicstaticToolResultsuccess(Object output,String base64Image){...}publicstaticToolResulterror(String error){...}}

5.2 Docker沙箱:安全的代码执行

SandboxToolDockerSandbox配合实现了在Docker容器内安全执行用户代码的能力。

容器配置强调安全隔离

publicstaticclassSandboxSettings{publicstaticString image ="python:3.12-slim";publicstaticString workDir ="/workspace";publicstaticString memoryLimit ="512m";// 内存限制512MBpublicstaticdouble cpuLimit =1.0;// CPU限制1核publicstaticint timeout =300;// 超时5分钟publicstaticboolean networkEnabled =false;// 禁用网络访问}

禁用网络是一个关键的安全决策——防止恶意代码外传数据或发起攻击。

支持多语言代码执行,使用Heredoc方式传递代码,避免了复杂的字符转义问题:

privateStringbuildCodeExecutionCommand(String code,String language){switch(language.toLowerCase()){case"python":returnbuildHeredocCommand(code,"python3");case"bash":return code;// Bash直接执行case"node":returnbuildHeredocCommand(code,"node");case"java":// Java需要先写文件、编译、再执行String javaHeredoc =buildHeredocToFile(code,"/tmp/Main.java");return javaHeredoc +" && cd /tmp && javac Main.java && java Main";default:thrownewIllegalArgumentException("Unsupported language: "+ language);}}privateStringbuildHeredocCommand(String code,String interpreter){String delimiter ="OPENMANUS_CODE_EOF_"+System.currentTimeMillis();returnString.format("%s << '%s'\n%s\n%s", interpreter, delimiter, code, delimiter);}

使用时间戳生成唯一的Heredoc分隔符(OPENMANUS_CODE_EOF_1718...),确保分隔符不会与代码内容冲突。

5.3 浏览器自动化:Playwright驱动

BrowserTool基于Playwright实现了完整的浏览器操作能力,支持7种操作:

操作说明
navigate导航到URL,等待网络空闲
click通过CSS选择器点击元素
type在输入框中输入文本
screenshot全页面截图,返回Base64编码
get_content提取页面标题、URL和文本内容
scroll上下左右滚动页面
wait等待元素出现或页面加载

浏览器采用懒加载模式,只在首次使用时初始化:

publicToolResultexecute(Map<String,Object> parameters){// 首次使用时才创建浏览器实例if(browser ==null){initializeBrowser();}// ...}privatevoidinitializeBrowser(){Playwright playwright =Playwright.create(); browser = playwright.chromium().launch(newBrowserType.LaunchOptions().setHeadless(false));// 非无头模式 context = browser.newContext(newBrowser.NewContextOptions().setViewportSize(1920,1080).setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...")); currentPage = context.newPage();}

截图功能是多模态能力的关键入口——Agent可以截图后让大模型"看到"页面内容:

privateToolResulthandleScreenshot(Map<String,Object> parameters){byte[] screenshot = currentPage.screenshot(newPage.ScreenshotOptions().setFullPage(true).setType(ScreenshotType.PNG));String base64Screenshot =Base64.getEncoder().encodeToString(screenshot);returnToolResult.success("截图成功", base64Screenshot);// 携带Base64图片}

5.4 网页搜索:Tavily集成

TavilySearchTool通过langchain4j的Tavily封装实现网页搜索:

publicToolResultexecute(Map<String,Object> parameters){String query =getString(parameters,"query");WebSearchResults results = searchEngine.search(query);List<Map<String,Object>> searchResults = results.results().stream().map(result ->Map.of("title", result.title(),"url", result.url(),"snippet", result.snippet())).toList();returnToolResult.success(response);}

六、完整执行流程:一个真实的例子

让我们通过入口程序的示例任务,完整追踪Agent的执行过程:

String prompt =""" 1. 创建一个名为'test_page.html'的HTML文件并添加内容 2. 使用file://协议在浏览器中打开本地文件 3. 给打开的页面截图 4. 告诉截图中的内容 """; manusAgent.run(prompt);

执行流程如下

┌─────────────────────────────────────────────────────────┐ │ Step0: 初始化 │ │ Memory:[SystemMessage,UserMessage] │ └──────────────────────────┬──────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ Step1: LLM推理 → 决定调用 write_file 工具 │ │ Action:write_file(path="workspace/test_page.html", │ │ content="<html>...</html>") │ │ Memory:+[AssistantMsg(tool_calls),ToolMsg(成功)] │ └──────────────────────────┬──────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ Step2: LLM推理 → 决定调用 browser.navigate │ │ Action:browser(action="navigate", │ │ url="file:///path/to/workspace/test_page.html") │ │ Memory:+[AssistantMsg(tool_calls),ToolMsg(导航成功)] │ └──────────────────────────┬──────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ Step3: LLM推理 → 决定调用 browser.screenshot │ │ Action:browser(action="screenshot") │ │ Memory:+[AssistantMsg(tool_calls), │ │ ToolMsg(截图成功 + base64Image)] │ └──────────────────────────┬──────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────┐ │ Step4: LLM推理 → 分析截图内容,返回文本描述 │ │ Response:"截图中显示了一个HTML页面,内容包括..." │ │ finish_reason:"stop" → 任务完成,退出循环 │ └─────────────────────────────────────────────────────────┘ 

在这个过程中,Agent展现了以下能力:

  1. 任务规划:将复合任务自动拆解为多个步骤
  2. 工具选择:根据当前步骤选择最合适的工具
  3. 结果反馈:每次工具执行后将结果传回LLM进行下一步推理
  4. 多模态理解:能够"看到"截图内容并进行描述
  5. 自主终止:完成所有子任务后主动结束

七、设计模式总结

回顾整个项目,我们可以提炼出以下核心设计模式:

设计模式应用位置作用
模板方法BaseAgent.run() + step()固定执行骨架,子类实现单步逻辑
策略模式RelevanceFilter接口可插拔的上下文过滤策略
工厂方法Message.userMessage()统一消息创建
注册表模式ToolCollection集中管理工具的注册和查找
懒加载BrowserTool/DockerSandbox按需初始化重量级资源
适配器模式OpenAIClient将内部模型适配为OpenAI API格式

八、进一步思考

这个项目虽然是学习性质,但其架构设计完全可以作为生产级Agent系统的基础。以下是一些可以进一步优化的方向:

  1. 流式输出:当前使用同步HTTP调用,可改为SSE流式响应,提升用户体验。
  2. 并行工具执行:当前系统提示词限制"一次只能执行一个工具",实际上OpenAI API支持在一次响应中返回多个tool_calls,可以并行执行。
  3. 持久化记忆:当前Memory是内存级别的,可引入向量数据库实现长期记忆。
  4. 更细粒度的错误恢复:当工具执行失败时,可以引入重试机制或备选方案。
  5. 安全增强:对FileWriter/FileReader添加路径白名单限制,防止任意文件读写。

结语

通过这个纯Java实现的AI Agent项目,我们深入理解了Agent的三个核心机制:

  • ReAct循环:推理与行动的交替执行,是Agent"思考-行动-观察"的基本范式
  • 工具调用协议:大模型通过Function Calling标准接口与外部工具交互
  • 上下文管理:在有限的上下文窗口内,通过相关性过滤保留最有价值的信息

AI Agent不是魔法,它本质上是一个以LLM为决策引擎、以工具为执行手段、以消息链为上下文的自动化循环系统。理解了这个本质,你就能根据自己的业务需求,构建出真正有用的智能体应用。


在这里插入图片描述

Read more

Alibaba低代码引擎完整实战指南:5步构建企业级可视化开发平台

Alibaba低代码引擎完整实战指南:5步构建企业级可视化开发平台 【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系 项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine 还在为传统开发的高复杂度而困扰吗?Alibaba低代码引擎为您提供了一套完整的企业级可视化开发解决方案。这套面向扩展设计的技术体系让开发效率提升10倍成为现实,无论您是前端初学者还是资深工程师,都能在这个强大的开发平台中找到适合自己的工作节奏。 为什么低代码正在重塑开发模式? 传统开发中,开发者需要编写大量重复代码,处理复杂的配置逻辑。而低代码引擎通过可视化拖拽和图形化配置,将开发过程简化为几个直观的步骤: * 组件化思维:将页面拆分为可复用的组件模块 * 可视化编排:通过拖拽方式组合页面结构 * 配置化开发:属性设置取代代码编写 环境配置:极速启动开发环境

By Ne0inhk

OpenClaw基础-3-telegram机器人配置与加入群聊

OpenClaw基础-3-telegram机器人配置与加入群聊 💡 大家好,我是可夫小子,《小白玩转ChatGPT》专栏作者,关注AI编程、AI自动化和自媒体。 Openclaw的优势是接入各种聊天工作,在前面的文章里,已经介绍了如何接入飞书。但之前我也提到了,飞书的最大的问题是请求多的限制,以及无法在非认证企业账号下面组建群聊。但这些限制另一个聊天工具可以打破,那就是Telegram,今天就跟大家分享一下,如果在OpenClaw里面接入Telegram。 第一步:Openclaw端配置 通过命令openclaw config,local→channels→telegrams 这里等待输入API Token,接下来我们去Telegram里面获取 第二步:Telegram端配置 1. 1. 在聊天窗口找到BotFather,打开对话与他私聊 2. 3. 然后再输入一个机器人,再输入一个账号名username,这里面要求以Bot或者Bot结尾,这个是全网的id,要 2. /newbot 来创建一个机器人,输入一个名字name

By Ne0inhk

NVIDIA Isaac Sim 结合 ROS2 在无人机室内导航的应用:从仿真到实战的全维度解析

前言:室内导航的技术困境与仿真革命 在天津某冷链物流中心的深夜,一架四旋翼无人机正试图穿过仅 0.8 米宽的货架通道。机腹的深度相机在低温下闪烁着蓝光,却因货架金属表面的反光产生了大量噪点。地面控制终端上,定位坐标如同醉酒般摇摆 —— 这不是设备故障,而是室内无人机导航面临的典型挑战。当 GPS 信号被混凝土墙体完全屏蔽,当 Wi-Fi 信号在密集货架间剧烈波动,当视觉传感器被光照变化和相似场景迷惑,无人机如何像在室外那样自如穿梭? 这个问题的答案藏在两个技术领域的交叉点上:高保真仿真平台与机器人操作系统。NVIDIA Isaac Sim 作为基于 Omniverse 的物理精确仿真环境,提供了从像素到牛顿的全尺度模拟能力;而 ROS2(Robot Operating System 2)则作为机器人控制的 "神经中枢",实现了感知、决策与执行的模块化协作。当这两者结合,不仅解决了室内导航算法开发的成本与风险问题,更构建了一条从虚拟测试到物理部署的无缝桥梁。 本文将以 4 万字篇幅,通过 50

By Ne0inhk
大模型+智能家居解决方案--小米MiLoco部署

大模型+智能家居解决方案--小米MiLoco部署

一、Miloco简介 小米推出了首个“大模型+智能家居”解决方案Xiaomi Miloco,全称为 Xiaomi Local Copilot(小米本地协同智能助手)。 https://gitee.com/xiaomi-miloco/xiaomi-miloco 1、GitHub地址 https://github.com/XiaoMi/xiaomi-miloco Miloco以米家摄像头为视觉信息源,以自研大语言模型MiMo-VL-Miloco-7B为核心,连接家中所有物联网(IoT)设备,框架面向所有人开源。MiMo-VL-Miloco-7B模型基于小米4月发布的MiMo模型调优而来,“天才少女”罗福莉最近加入的正是MiMo模型团队。 这很可能是智能家居的“ChatGPT时刻”,小米AIoT平台截至今年6月已连接的IoT设备数(不含智能手机、平板及笔记本计算机)达9.89亿台,数以亿计的米家摄像头、小爱音箱、台灯等设备都有望用上大模型。 从小米公布的Miloco页面来看,页面主视觉是一个类似于ChatGPT的聊天框,聊天框的左侧具有智能家居设备的导航栏,包括AI中心、模型管

By Ne0inhk