☰
用Java从零构建AI Agent框架:JavaManus核心设计与实践
2026/10/27 3:12:27 网站建设 项目流程

先说结论:Java 不是做 AI Agent 的最优选择,但如果你所在的团队 Java 技术栈是硬约束,那么用 Java 从零搭一个 Agent 框架,反而比硬套 Python 那一套走得更远。JavaManus 就是这么来的——我用纯 Java 重新实现了 Manus 这类通用 AI Agent 的核心链路,包括任务规划、工具调用、多轮记忆和上下文管理,框架不依赖 LangChain4j 之类的现成上层封装,核心循环全部自己实现。这篇文章把设计和实现拆开聊,适合两类人:想在 Java 生态里落地 Agent 的工程师,以及想彻底搞懂 Agent 框架底层原理的开发者。

1. 为什么用 Java 重新发明一个 Agent 轮子

1.1 Python 的 Agent 生态很热闹,但 Java 这边是真空

AI Agent 这个概念最近两年被聊得火热,你打开 GitHub 搜 agent,出来的几乎是清一色的 Python 项目:LangChain 靠抽象链绑定了一批用户,CrewAI 主打多角色编排,AutoGen 让多个对话智能体相互辩论,Dify 则把 RAG 和 Agent 包装成一站式应用平台。再看企业级落地场景,Java 工程师普遍处于一个尴尬位置:想抄作业都找不到同语言的范本。

不是 Java 做不了 Agent,而是这个领域的开源惯性全在 Python 那边。大模型厂商的 SDK 优先出 Python,社区的教程、示例、踩坑笔记也默认是 Python。Java 开发者想做个 Agent,要么起一个 Python 旁路服务做代理,要么硬着头皮用 LangChain4j、Spring AI 这类还在快速迭代的 Java 封装。旁路服务的问题在于两套技术栈的部署、监控、联调成本全翻倍;现成封装的痛点是抽象层级太高,出了诡异问题不好排查,想定制一个特殊的 Agent 循环往往要翻源码、打补丁。

JavaManus 的出发点就是填这个空。它不追求给 LangChain 那样的“全家桶”式抽象,而是把通用 Agent 最核心的那条链路——理解用户目标、拆解任务步骤、调用外部工具、根据结果继续决策——在 JVM 上完整打通。框架本身是开源的,你可以直接拿去扩展,也可以只把它当成一份 Java 实现 Agent 的参考教材。

1.2 虚拟线程、类型系统与 Java 生态:这些优势被低估了

很多人觉得 Java 写 AI 应用一定很笨重,其实在 Agent 这个场景里,Java 有几个被低估的优势。

首先是类型安全。Agent 的工具调用本质上是对外部函数的一次动态调度,Python 靠字典和鸭子类型把参数传来传去,出错了要等运行时才能暴露;Java 可以用 record、泛型、反射和 JSON Schema 在进入模型之前就把工具的参数结构定义清楚,编译期能扫掉一大批低级错误。

其次是并发模型。Agent 执行过程中最耗时的操作几乎全是 IO 等待:等大模型返回、等浏览器渲染完页面、等 Shell 命令执行完。JDK 21 引入的虚拟线程让 Java 终于能用“近似无限线程”的方式写阻塞式代码,而不是在 CompletableFuture 的 callback 地狱里挣扎。你完全可以给每个工具调用开一个虚拟线程,成本低到可以忽略,这在 Java 20 之前是难以想象的。

第三个是生态集成。企业里现成的监控链路(Prometheus、SkyWalking、ELK)、权限体系(Spring Security)、配置中心、消息队列,全是 Java 系的。Agent 要做成产品,不能只在本地跑一个 Demo,要和这些基础设施打通,Java 具备天然优势。Python 方案往往需要额外维护一套独立的观测和治理体系。

当然,Java 也有明显的短板:AI 相关的库少、社区案例少、出问题能搜到的中文资料更少。但这恰恰是这篇文章想解决的——把链路讲透,让后来者少踩坑。

1.3 JavaManus 的定位:不是克隆 Manus,而是提炼 Agent 内核

这里必须澄清一点:JavaManus 并不是把 Manus 的前端页面和云端调度完整搬过来。Manus 真正值得学习的不是它那些花哨的演示视频,而是它背后那个通用 Agent 的产品逻辑——用户只需要给一个目标,Agent 自己规划步骤、调用工具、输出结果。

JavaManus 提炼的是这个逻辑的内核:一个可嵌入任何 Java 应用的 Agent 运行时。它包含四个核心抽象——Planner(规划器)、Executor(执行器)、ToolRegistry(工具注册表)、Memory(记忆模块)。这四个抽象构成了一个最小的通用 Agent 闭环。你拿它可以做代码分析 Agent、运维巡检 Agent、数据查询 Agent,玩法取决于你注册了哪些工具。这种定位让框架保持轻量,不被具体业务绑架。

2. JavaManus 的整体架构与 Agent 循环设计

2.1 分层架构:从 LLM 接入到工具执行的边界划分

JavaManus 在设计上严格划分了五层,每一层只依赖下一层的接口,不跨层调用。这样做的目的是让每一层都能独立替换:今天用通义千问,明天换 DeepSeek,后天接企业内部私有化模型;今天工具只有三个,明天扩展到三十个,框架本体都不需要动。

最外层是接入层,提供统一的 Agent 入口;往里是编排层,包含规划器和执行器,负责跑 Agent 主循环;再往里是工具层,管理所有可被大模型调用的工具;第四层是记忆层,管理短期对话历史和长期工作区文件;最底层是基础设施层,封装 LLM 客户端、Token 计算、日志与指标采集。

每一层都定义成接口。比如 LLM 客户端不是直接绑定某个厂商的 SDK,而是定义成一个ChatClient接口,内部用 OpenAI 兼容协议对接不同供应商。这样底部模型供应商更换时,上层业务代码完全无感。这个边界划分是我一开始就坚持的:宁可多写几层接口,也不要让模型厂商的 SDK 泄漏到业务代码里。

2.2 核心 Agent 循环:先规划、再执行、边观察边修正

JavaManus 的 Agent 循环是 ReAct 范式和 Plan-and-Execute 范式的结合。ReAct 是“思考-行动-观察”的交替循环,每一步都基于前一步的结果重新决策;Plan-and-Execute 是先做一版完整计划,再按计划执行。两者各有优劣:ReAct 灵活但容易迷失方向,计划执行稳定但应变不足。

实际实现时,JavaManus 在每一轮循环内部做了一次“迷你规划”。大模型拿到用户目标后,先输出本轮要调用的工具和参数,框架执行工具后把结果回填给模型,模型观察结果再决定下一步调用什么工具,直到它认为目标已完成,输出最终答复。整个过程的控制权始终在 Agent 循环手里,而不是一次性生成一堆工具调用扔给系统。

这个循环最核心的代码其实很短:

while (turn < maxTurns && !cancelled) { List<AgentMessage> context = memory.buildContext(userGoal); ChatResponse response = chatClient.chat(context, toolRegistry.schemas()); if (response.hasToolCalls()) { for (ToolCall call : response.toolCalls()) { ToolResult result = toolRegistry.execute(call); memory.append(AgentMessage.fromToolResult(call.id(), result)); } } else { finalAnswer = response.content(); break; } turn++; }

这个循环看起来简单,但真正的难点藏在细节里:每一轮该保留多少历史消息、工具调用出错怎么恢复、模型陷入重复调用怎么终止、上下文快塞满时怎么压缩。这些才是 Agent 框架真正值钱的地方。

2.3 并发模型:虚拟线程 + CompletableFuture 的编排策略

Agent 执行过程中最典型的并发场景有两个:一个是多个互不依赖的工具调用并行执行,另一个是用户在等待 Agent 响应时执行超时取消。

JavaManus 的默认策略是:每个 Agent 实例运行在独立的虚拟线程里,工具调用级的并行用 CompletableFuture 编排。原因很简单——虚拟线程让代码保持同步风格,可读性和可调试性都高;而 CompletableFuture 负责真正需要并行的部分,比如同时读取多个配置文件、并行请求多个数据源。

我实测下来的经验是,虚拟线程对 Agent 场景是质的提升。早期用平台线程时,一个 Agent 实例要占一个线程,开 50 个并发 Agent 就要专门调线程池参数;换成虚拟线程后不需要线程池,开了就用,用完即弃,系统资源占用下降了一个数量级。不过要注意,虚拟线程上不能执行synchronized包裹的长时间阻塞操作,否则会钉住 carrier 线程,这属于虚拟线程的进阶坑,第五节细说。

3. 底层技术拆解:LLM 接入与 Function Calling 实现

3.1 为什么自己封装 LLM 客户端,而不是直接上 LangChain4j

这是我被问到最多的问题:“Spring AI 不香吗?LangChain4j 不也挺好吗?”当然香,Java 生态绕不开它们。但 JavaManus 的定位是“从零构建一个 Agent 框架”,如果直接用 LangChain4j,核心链路会被它的抽象盖住,达不到学习框架原理的目的。从工程角度讲,LangChain4j 的 Agent 抽象还很年轻,升级频繁、Breaking Change 多,而 JavaManus 需要的是稳定可控的最小内核。

所以我选择自己封装一层极薄的 LLM 客户端。薄到什么程度?只做三件事:构造 HTTP 请求体、调用 OpenAI 兼容的/chat/completions接口、解析响应。请求体里最关键的是tools数组和tool_choice字段,这是 Function Calling 的核心。生产环境接入时,这层可以无缝替换成 Spring AI 或者厂商 SDK,因为接口边界已经定好了。

自己封装还有一个额外的好处:你能清楚看到 Token 是怎么消耗的。每次请求发出去的messages体积、返回的tool_calls内容、累计的成本,全部可以自定义埋点。用别人封装好的库,这些细节往往被藏得很深。

3.2 Function Calling 的协议解剖:JSON Schema 到 Java 反射

Function Calling 的本质,是把 Java 的工具方法翻译成一份大模型能理解的 JSON Schema 描述,然后让大模型返回“我想调用哪个工具、参数是什么”。比如一个统计代码行数的工具,它的 Schema 大概是这样的:

{ "type": "function", "function": { "name": "code_stats", "description": "统计指定目录下所有 Java 文件的代码行数", "parameters": { "type": "object", "properties": { "path": {"type": "string", "description": "项目绝对路径"} }, "required": ["path"] } } }

大模型收到这份 Schema 后,如果判断需要统计代码行数,就会在响应里返回类似这样的结构:

{ "tool_calls": [{ "id": "call_xxx", "function": { "name": "code_stats", "arguments": "{\"path\": \"/home/user/project\"}" } }] }

Java 侧要做的事情,就是把这段arguments字符串解析成 Java 方法参数,反射调用对应的工具方法。这里有个很容易踩的坑:arguments是一个字符串,不是结构化对象,必须先反序列化成JsonNode再适配到方法签名。如果工具方法参数和 JSON 字段类型不匹配,或者布尔值传成了字符串,都会在运行时才暴露。

3.3 工具注册与参数校验的工程化设计

为了让第三方开发者方便地给 Agent 添加技能,JavaManus 设计了注解驱动的工具注册机制。你只需要写一个普通类,在方法上标记@AgentTool注解,启动时框架会自动扫描、生成 Schema、注册到工具表。大概长这样:

public class CodeTools { @AgentTool( name = "code_stats", description = "统计指定目录下所有 Java 文件的代码行数" ) public Map<String, Object> codeStats( @ToolParam(description = "项目绝对路径") String path ) { // 业务逻辑 } }

框架启动时通过反射读取注解和参数信息,自动构建出上一节那个 JSON Schema。这里的工程细节有三点必须做好:

第一是参数描述必须准确,模型靠 description 决定怎么填参数,描述含糊会导致模型传错参数;第二是必须定义参数白名单,不能允许模型传入任意对象,否则有被注入攻击的风险;第三是要做统一的结果包装,把自定义异常转成模型能理解的错误信息,而不是把 Java 异常堆栈直接丢给大模型。错误信息一旦乱,模型下一步决策就会跟着乱。

4. 核心模块代码解析:消息、规划器、执行器、记忆

4.1 工程骨架与依赖:JDK 21 + Gradle

JavaManus 的基础要求是 JDK 21,因为要用虚拟线程和新的 record 特性。构建工具用的 Gradle,依赖保持极简,除了 Jackson(处理 JSON)、一个轻量 HTTP 客户端(用的是 JDK 内置的HttpClient),几乎没有第三方依赖。这种极简风格的另一个好处是:框架不会被依赖冲突拖累,放在任何 Java 项目里都是安全的。

工程目录结构大致如下:

src/main/java/io/javamanus/ ├── core/ │ ├── Agent.java │ ├── AgentMessage.java │ ├── ToolCall.java │ └── ToolResult.java ├── planner/ │ ├── AgentPlanner.java │ └── ReactivePlanner.java ├── executor/ │ └── AgentExecutor.java ├── tool/ │ ├── AgentTool.java │ ├── ToolRegistry.java │ └── ToolParameter.java ├── memory/ │ ├── ShortTermMemory.java │ └── WorkspaceMemory.java └── llm/ ├── ChatClient.java ├── OpenAiChatClient.java └── ChatResponse.java

模块之间的依赖是单向的:Agent依赖AgentPlanner和AgentExecutor,AgentExecutor依赖ToolRegistry和ChatClient。记忆模块是横切面,各模块都能访问。这样分层的目的是:你想换一个规划器实现,或者换一个 LLM 客户端实现,都只影响一个子模块。

4.2 消息模型与 Token 管理

Agent 对话和历史记录统一用AgentMessagerecord 表示,它承载四种角色:系统消息、用户消息、助手消息、工具结果消息。工具结果的关联是关键,每条工具结果消息都要携带对应工具调用的id,这样大模型才能把结果和之前的调用对上:

public record AgentMessage( Role role, String content, List<ToolCall> toolCalls, String toolCallId ) { public enum Role { SYSTEM, USER, ASSISTANT, TOOL } }

Token 管理是 Agent 工程化的隐藏难点。每轮循环都要往请求里塞越来越多历史消息,不控制迟早会撑爆模型上下文窗口。JavaManus 的默认策略是:估算所有消息的总 Token(用content.length() / 4近似),当占用超过上下文窗口的 70% 时触发压缩。压缩不是简单截断,而是把早期历史交给模型生成一段摘要,再替换成“摘要 + 最近 N 轮完整消息”的结构。

实际使用时,我建议在 Agent 实例上显式配置maxContextTokens和maxTurns,让框架在接近边界时自动终止或降级。宁可让 Agent 多做一轮调用,也不要让它带着残缺的历史继续决策——模型决策质量对上下文完整性非常敏感。

4.3 规划器与执行器的实现细节

规划器接口定义得很轻:输入用户目标,输出一个步骤列表。但实现上有个关键权衡——一次规划得越细,越容易失败;一次规划得太粗,又失去了规划的意义。JavaManus 的做法是“颗粒度自适应”:简单任务(比如“解释一下这个报错”)不规划,直接让模型回答;中等任务(比如“给这个项目生成一份 README”)拆成 3-5 个步骤;复杂任务才拆成子任务列表逐步执行。

执行器是整个循环的心脏。除了基础循环,它还负责三个边界问题:第一是终止条件,默认最大轮数 20,超过后强制终止并要求模型基于已有信息输出结果;第二是重复调用检测,如果模型连续 3 次调用同一个工具、传同样参数,说明它陷入了死循环,需要打断并提示它换个思路;第三是异常恢复,工具抛异常时,把异常信息封装成错误结果回传给模型,让模型自己决定是换参数重试还是换方案。

这三种边界处理是 Agent 工程化的关键,少了任何一个,线上跑起来都会出现不可控的“僵尸 Agent”。

4.4 记忆模块:短期窗口 + 长期工作区

Agent 的记忆分两层。短期记忆就是消息列表,包含用户提问、模型回答、工具调用和结果;长期记忆则是“工作区”,即 Agent 在执行任务时产生的文件、报告、中间数据。JavaManus 给每个 Agent 实例分配一个独立工作区目录,Agent 可以读文件、写文件,这些文件跨轮次存在,相当于它的“草稿纸”。

实际设计短期记忆时,我建议把它做成可插拔接口。内存版适合单机 Demo,Redis 版适合多实例部署,未来扩展向量库版就能给 Agent 加上长期事实记忆。这里没有绝对最优,只有场景匹配。重要的是接口不要绑定具体存储实现,否则 Agent 想要记忆升级的时候,你会发现所有代码都要跟着改。

5. 实战:让 JavaManus 自动完成代码仓库分析

5.1 任务场景与模型配置

理论说再多,不如跑一个真实任务。我用 JavaManus 做了一个实验:让 Agent 自动分析一个 Java 项目的代码规模,找出最大的几个类,并生成一份 Markdown 报告。这个任务包含典型的多步协作:统计代码行数、读取关键文件、整理结果、写入报告文件。

模型配置上,我选用支持 Function Calling 的 OpenAI 兼容接口,参数如下:

javamanus: llm: base-url: ${LLM_BASE_URL} api-key: ${LLM_API_KEY} model: qwen-plus temperature: 0.1 max-tokens: 2048 agent: max-turns: 20 max-context-tokens: 12000 workspace: ./workspace

温度设置 0.1 是工具型 Agent 的黄金值。温度太高模型会“发挥创意”,在工具调用上表现成参数名写错、闭包多个空格之类的低级失误。Agent 场景要的是确定性,不是创造性。

5.2 从零写一个 Agent 工具:仓库统计器

我先注册了一个工具code_stats,它负责遍历目录、统计 Java 文件数和代码行数。核心实现很简单,这里展示工具注册部分的代码:

@AgentTool( name = "code_stats", description = "统计指定目录下的 Java 文件数量、总代码行数和最大的 5 个类" ) public Map<String, Object> codeStats( @ToolParam(description = "项目根目录的绝对路径") String path ) { Map<String, Object> result = new HashMap<>(); List<FileStats> fileStatsList = new ArrayList<>(); try (Stream<Path> paths = Files.walk(Path.of(path))) { paths.filter(p -> p.toString().endsWith(".java")) .forEach(p -> fileStatsList.add(countLines(p))); } result.put("totalFiles", fileStatsList.size()); result.put("totalLines", fileStatsList.stream().mapToInt(FileStats::lines).sum()); result.put("topClasses", fileStatsList.stream() .sorted((a, b) -> b.lines() - a.lines()) .limit(5) .toList()); return result; }

注意工具描述里明确说了“最大的 5 个类”,这样模型拿到结果后可以直接引用,不需要再猜。这是写工具描述的一个技巧:描述里的信息粒度要和模型决策需要的粒度匹配。

5.3 运行过程还原:观察 Agent 的决策轨迹

运行起来之后,把每一步日志打开,你会看到一段很有意思的决策轨迹:

第一轮,模型判断任务需要统计代码量,调用code_stats工具,参数是/home/user/demo-project;工具返回统计结果,包含 128 个文件、23000 行总代码、最大类是OrderServiceImpl.java(1800 行)。第二轮,模型看到最大类是订单服务实现类,判断“读一下这个文件,看看为什么这么大”,于是调用read_file工具读取文件前 200 行。第三轮,模型看完代码后,开始组织报告:先写项目总体规模,再分析大类的可能原因,最后给出重构建议。第四轮,模型判断需要一个输出文件,调用write_file工具把报告内容写入工作区的REPORT.md。第五轮,模型确认任务完成,输出最终答复:“已生成代码分析报告,位于 workspace/REPORT.md”。

这五轮里,模型的每一步都是基于上一步结果的真实决策,而不是预置脚本。这就是 Agent 和 “自动化脚本” 的本质区别:脚本是提前编排好的,Agent 是临场决策的。你给它换个新任务,它不需要改代码,只需要换一个目标描述。

5.4 参数调优经验表

结合多次实测,我把关键参数的建议值整理如下:

参数推荐值说明
temperature0.1工具调用需要确定性,过高会随机写错参数
max-turns20超过 20 轮基本说明 Agent 在绕圈,强制终止
max-context-tokens模型窗口的 70%留出响应空间,触发压缩不要紧
工具超时60s外部调用超过 60s 判失败,虚拟线程可安全取消
并行工具调用false初期关掉,避免工具结果顺序错乱

这些参数没有绝对真理,只有工程经验的取舍。比如温度,如果你做的是文案生成 Agent,0.7-0.9 会更合适;但工具型 Agent 最好低一些。参数调整之后,一定要跑同一组回归用例对比效果,不然调参就是玄学。

6. 踩坑实录:Agent 框架开发中的 6 个典型问题

6.1 模型输出格式漂移:Function Calling 偶尔不听话

即使已经声明了tools,仍有小概率模型不返回结构化tool_calls,而是把“我想调用 xx 工具”写进普通文本里。这可能是模型不支持 Function Calling,也可能是提示词让它产生了误解。排查手段是:检查响应里是否包含tool_calls字段,如果内容是文字描述而不是结构化调用,这轮就作为普通文本消息回填给模型,同时追加一条系统提示:“如果你需要使用工具,必须返回 tool_calls,不要描述调用过程。”实测下来,多数模型在收到这条提示后会恢复正常。

另一个相关坑是参数格式漂移,比如 path 写成"path": "/home/user/"带了尾部空格。解决办法是在工具执行前做参数规整:trim 字符串、校验必填字段、类型转换失败时返回明确错误信息。错误信息要让模型能理解,而不是抛一个 Jackson 解析异常。

6.2 工具执行超时与死循环

最常见的线上事故就是 Agent 卡在某一步不动了。原因通常是外部工具没有设置超时,比如 HTTP 请求没有 timeout,Shell 命令一直不退出。JavaManus 的处理是双重超时:每个工具调用由独立的虚拟线程执行,外层用Future.get(timeout)控制;同时记录工具调用次数,超过阈值自动终止循环。

死循环则表现为模型反复调用同一工具。我在日志里见过模型连续 8 次调用read_file且参数完全相同的案例,这就是模型陷入了重复执行状态。解法是加“循环检测”:记录最近的工具调用 hash,连续重复 3 次就注入一条提醒消息,让模型反思是不是应该换一种思路。

6.3 上下文爆炸与 Token 超限

Agent 跑长任务时,每一轮都会累积工具结果。有些工具结果非常长,比如read_file读一个 500 行的文件,结果直接塞进消息历史,一两轮就需要几万 Token。这是 Agent 开支最大的地方,也是代码质量问题的高发区。

我的经验是给大型工具结果做“摘要化”。比如read_file工具可以加一个参数maxLines,模型默认只需要读前 200 行;code_stats工具返回的topClasses限制 5 个。工具结果在写入记忆前还可以做结构化截断:超过阈值的结果只保留头部 + 尾部 + 长度说明。Agent 不需要每条信息都完整,它需要的是“足够决策的信息”。

6.4 虚拟线程上的同步阻塞陷阱

虚拟线程的坑很容易踩:在虚拟线程里调用synchronized同步块时,如果线程被阻塞,会出现“钉扎”现象——占用的 carrier 线程不释放,虚拟线程的数量优势就白搭了。

JavaManus 在早期版本里给工具执行加了synchronized锁来做注册表并发控制,压测时发现大量虚拟线程卡死。正确做法是改用ReentrantLock或者并发容器如ConcurrentHashMap,避免在虚拟线程内长时间持有类锁。另一个经验是,JDBC 这类基于同步 IO 的驱动在虚拟线程里表现不理想,如果是重度数据库访问的工具,建议配独立线程池隔离,或者直接换异步驱动。

6.5 多轮会话记忆串台与消息顺序错乱

一个 Agent 实例服务多个用户时,记忆串台是容易踩到的大坑。工具调用异步完成后,如果结果回填顺序错乱,模型看到的“历史”就是错乱的,决策当然不准。JavaManus 的做法是:整个 Agent 循环在单虚拟线程内串行执行,用内存队列保证消息顺序,工具调用可以并发执行,但回填必须按调用顺序完成。

还有一种顺序错乱来自消息角色:工具结果消息必须紧跟对应的助手消息。如果中间插入了别的用户消息,模型会懵。因此每次构造上下文时,我会显式校验消息链的合法性,不合法时宁可丢弃旧的工具结果、让模型重新调用。

6.6 常见问题速查表

现象可能原因解决办法
模型不返回 tool_calls模型不支持工具调用改用支持 Function Calling 的模型,或降级为文本解析
工具参数经常传错参数描述不准确精炼工具描述,给每个参数加示例值
Agent 陷入重复调用缺少循环检测加重复调用判断,超 3 次注入反思提醒
上下文 Token 超限工具结果过大工具返回做摘要,结果写入前截断
虚拟线程卡死synchronized 阻塞换成 ReentrantLock 或并发容器
工具结果顺序错乱并发回填未控制串行回填,按调用顺序追加消息
多用户记忆串台状态未隔离每个用户/会话独立 Agent 实例 + 独立记忆

这套排查思路基本覆盖了 Agent 框架从开发到上线的常见问题。每个问题背后都有对应的防御机制,这也是框架存在的意义:不是把决定全交给大模型,而是用工程手段给模型的自由发挥划上边界。

代码跑通之后,我自己感受最深的一件事是:Agent 框架的核心不是某个惊艳的抽象设计,而是把“决策循环”这条主线做好,让每一次工具调用都有迹可循、可回放、可终止。JavaManus 后续我准备继续补上基于图的规划器(Graph-based Planner)和更细粒度的任务回滚机制。如果你也在用 Java 搞 Agent,欢迎把踩到的坑丢给我,有些坑只有踩过才知道。最后分享一个小技巧:调试 Agent 时,把每一步工具调用的输入输出都落到日志里,你会回来感谢我的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询