推荐两款Java AI开源项目:RuoYi-AI v3.0与JavaClaw深度解析

推荐两款Java AI开源项目:RuoYi-AI v3.0与JavaClaw深度解析从 能聊天 到 能干活 Java AI Agent 的进化之路 在 AI 应用开发领域 2024 2025 年见证了从简单对话到智能代理 Agent 的重大范式转变 当 OpenClaw 在 GitHub 上掀起热潮 Python Rust Go C 等语言纷纷涌现出相关实现时 Java 生态却长期处于空白状态 直到最近 两款重量级 Java

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



从”能聊天”到”能干活”——Java AI Agent 的进化之路

在AI应用开发领域,2024-2025年见证了从简单对话到智能代理(Agent)的重大范式转变。当OpenClaw在GitHub上掀起热潮,Python、Rust、Go、C#等语言纷纷涌现出相关实现时,Java生态却长期处于空白状态。

直到最近,两款重量级Java AI开源项目横空出世:

  1. RuoYi-AI v3.0:历时半年打磨,实现了从”对话”到”行动”的关键跨越
  2. JavaClaw:JobRunr作者新作,填补了Java版OpenClaw的生态空白

本文将深入剖析这两个项目的设计理念、核心架构和实战案例,帮助Java开发者快速掌握AI Agent开发的最新实践。


1.0时代:套壳时代 —— 能说不能做

特征:简单套壳,单纯调用LLM做返回

用户输入 → LLM API → 文本返回 → 用户

这是大多数AI应用的起点。一个聊天框,一个API调用,就能提供”智能对话”能力。

局限性

  • ❌ 无知识:无法获取企业数据
  • ❌ 无行动:只能”说”不能”做”

那时的开发者还在思考:AI到底能为企业做什么?

2.0时代:知识增强时代 —— 能看能连

特征:引入RAG + MCP,具备外部数据交互能力

用户输入 → 向量检索(RAG) → LLM + 知识库 → 返回增强结果

 ↓ MCP工具调用 → 外部数据源

核心能力

  • RAG(检索增强生成):让AI"看见"企业知识库
  • MCP(模型上下文协议):让AI"连接"外部系统

新的困境:复杂工作流只能依靠流程编排,按固定节点执行:

用户请求 → 节点A → 节点B → 节点C → 输出 (固定) (固定) (固定)

这种方式的问题:

  • ✅ 可控、可预测
  • ❌ 僵化、无法适应变化
  • ❌ 每个场景需要重新编排
  • ❌ 无法处理意料之外的问题

关键认知:真正的智能不应该是"预设的路径",而应该是"自主的决策"。

3.0时代:智能体时代 —— ReAct范式完整实现

特征:思考→行动→观察循环,具备自主决策能力

这是ReAct(Reasoning + Acting)范式的完整实现:

┌─────────────────────────────────────────┐ 

│ ReAct 循环 │ │ │ │ ┌──────────┐ ┌──────────┐ │ │ │ Thought │───→│ Action │ │ │ │ (思考) │ │ (行动) │ │ │ └──────────┘ └────┬─────┘ │ │ ↑ │ │ │ │ ↓ │ │ ┌────┴────┐ ┌──────────┐ │ │ │ Final │ │Observation│ │ │ │ Answer │ │ (观察) │ │ │ └─────────┘ └──────────┘ │ └─────────────────────────────────────────┘

ReAct四步循环详解

步骤 说明 示例 Thought(思考) 分析当前状态,决定下一步做什么 “用户要查询销售数据,我需要先获取数据库表结构” Action(行动) 执行具体操作 调用 queryTableSchema 工具 Observation(观察) 获取行动后的结果反馈 “表结构返回了,包含sales_table” Final Answer 信息足够时停止循环,给出最终输出 返回查询结果和图表

整体架构图

┌─────────────────────────────────────────────────────────┐ │ SupervisorAgent │ │ (任务协调器) │ ├─────────────────────────────────────────────────────────┤ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │ │ │SqlAgent │ │ChartAgent│ │SkillsAgent│ │WebAgent │ │ │ │(数据库) │ │(图表) │ │(技能) │ │(浏览器) │ │ │ └──────────┘ └──────────┘ └──────────┘ └─────────┘ │ │ │ │ │ │ │ │ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌───▼────┐ │ │ │DB Tools │ │ECharts │ │MCP Tools│ │Playwright│ │ │ └─────────┘ └─────────┘ └─────────┘ └────────┘ │ └─────────────────────────────────────────────────────────┘

核心代码实现

public interface SqlAgent {

@SystemMessage(""" You are an intelligent database query assistant. CRITICAL REQUIREMENT: 1. Think about what information you need 2. Take action by calling appropriate tools 3. Observe the results 4. Repeat until you have enough information 5. Provide final answer """) @UserMessage("Answer: {{query}}") @Agent("Intelligent database query assistant...") String getData(@V("query") String query); 

}

// 构建监督者 Agent - 管理多个子 Agent SupervisorAgent supervisor = AgenticServices.supervisorBuilder()

.chatModel(plannerModel) .listener(new SupervisorStreamListener(null)) .subAgents(skillsAgent, searchAgent, sqlAgent, chartGenerationAgent, echartsAgent) // 加入历史上下文 - 使用 ChatMemoryProvider 提供持久化的聊天内存 .chatMemoryProvider(memoryId -> createChatMemory(chatRequest.getSessionId())) .responseStrategy(SupervisorResponseStrategy.LAST) .build();

支持的编排模式

  1. 顺序工作流(Sequential workflow)
  2. 循环工作流(Loop workflow)
  3. 并行工作流(Parallel workflow)
  4. 条件工作流(Conditional workflow)
  5. 纯代理(Pure agents)

支持的输出模式

public enum SupervisorResponseStrategy { / * 使用内部LLM对最后响应和交互摘要进行评分, * 返回得分较高的那个 */ SCORED, / * 返回监督者与子代理交互的摘要 */ SUMMARY, / * 仅返回最后调用的子代理的最终响应(默认) */ LAST 

}

案例一:智能图表生成

用户请求:”查询chat_model表格返回厂商模型统计图表”

ReAct执行过程

[Thought 1] 用户需要数据统计和图表,我先查询数据库 [Action 1] 调用 queryAllTables → 获取表列表 [Observation 1] 发现 chat_model 表存在

[Thought 2] 需要了解表结构才能写SQL [Action 2] 调用 queryTableSchema("chat_model") [Observation 2] 获取到字段:vendor, model_type, model_name…

[Thought 3] 现在可以查询统计数据了 [Action 3] 调用 executeSql("SELECT vendor, COUNT(*)…") [Observation 3] 查询结果:ppio厂商2个模型,对话/向量各1个

[Thought 4] 数据已获取,生成图表配置 [Action 4] 调用 ChartGenerationAgent [Observation 4] 生成ECharts饼图JSON配置

[Final Answer] 返回图表给用户

关键价值:Agent自主完成6步推理决策,无需人工编排流程,自动完成从数据查询到图表生成的全链路。

案例二:Skills创建Excel文件

用户请求:”帮我创建一个员工工资excel表格并且计算总额”

ReAct执行过程

[Thought 1] 用户需要创建Excel,这是文档处理任务 [Action 1] 调用 SkillsAgent → activate_skill("xlsx") [Observation 1] xlsx技能已激活,可用openpyxl处理

[Thought 2] 需要生成带公式的工资报表 [Action 2] 编写Python代码创建Excel [Observation 2] 文件已生成:sales_report.xlsx

[Final Answer] 返回文件下载链接

核心代码实现

public interface SkillsAgent {

@SystemMessage(""" 你是一个文档处理技能助手。 可用技能: docx: Word文档处理 pdf: PDF文档处理 xlsx: Excel电子表格处理 """) @UserMessage("{{query}}") @Agent("文档处理技能助手...") String process(@V("query") String query); 

}

// ========== LangChain4j Skills 基本用法 ========== // 通过 SKILL.md 文件定义,LLM 按需通过 activate_skill 工具加载 Path skillsPath = Path.of(userDir, "ruoyi-admin/src/main/resources/skills"); List skillsList = FileSystemSkillLoader.loadSkills(skillsPath);

ShellSkills skills = ShellSkills.from(skillsList);

// 构建子 Agent: SkillsAgent - 负责文档处理技能(docx、pdf、xlsx) SkillsAgent skillsAgent = AgenticServices.agentBuilder(SkillsAgent.class)

.chatModel(plannerModel) .systemMessage("You have access to the following skills: 

"

 + skills.formatAvailableSkills() + " 

When the user’s request relates to one of these skills, "

 + "activate it first using the `activate_skill` tool before proceeding.") .toolProvider(skills.toolProvider()) .build(); 

Skills系统特点

  • 通过SKILL.md文件自然语言定义技能
  • 延迟加载优化Token消耗
  • 约定优于配置,零代码扩展

⚠️ 安全警告:Shell执行本质上是不安全的。命令直接在主机进程环境中运行,无需沙箱化、容器化或权限限制。建议仅在完全信任输入的受控环境下使用,生产环境应考虑沙箱隔离。

案例三:浏览器自动化

用户请求:"访问 http://localhost:5666/ 登录系统 用户名:admin/admin123 打开模型管理 搜索baai/bge-m3 然后点击编辑按钮查看详情 并且输出"

ReAct执行过程

[Thought 1] 需要访问指定网页并进行登录操作 

[Action 1] 调用 browser_navigate("http://localhost:5666/") [Observation 1] 页面已加载,等待登录表单

[Thought 2] 需要获取当前页面快照,识别登录元素 [Action 2] 调用 browser_snapshot [Observation 2] 页面结构已获取,发现用户名/密码输入框

[Thought 3] 执行登录操作 [Action 3] 调用 browser_run_code 填写表单并提交 [Observation 3] 登录成功,进入系统主页

[Thought 4] 导航到模型管理页面 [Action 4] 调用 browser_navigate 或点击菜单 [Observation 4] 已进入模型管理列表页

[Thought 5] 搜索目标模型 [Action 5] 在搜索框输入 "baai/bge-m3" 并搜索 [Observation 5] 找到目标模型记录

[Thought 6] 点击编辑按钮查看详情 [Action 6] 调用 browser_click 定位编辑按钮 [Observation 6] 详情页面已打开,获取并输出内容

[Final Answer] 返回模型详情信息

技术实现:Playwright浏览器自动化插件提供了8个工具方法:

  • navigateTo:打开网页
  • clickElement:点击元素
  • fillInput:填写输入框
  • getText:提取文本
  • takeScreenshot:截图
  • evaluateJavaScript:执行JS
  • waitForSelector:等待元素出现
  • closeBrowser:关闭浏览器

实现细节

  • 返回的网页文本被截断到1万字符以内,防止撑爆上下文
  • Playwright实例是懒初始化的,首次使用时才启动浏览器并自动安装Chromium

ReAct循环面临的挑战

单纯的”思考→行动→观察”循环在短任务中表现良好,但长任务面临挑战:

问题 描述 示例 无限循环 Agent陷入重复尝试无法退出 工具调用失败后反复重试 重复失败 相同错误反复出现 SQL语法错误不会自动修正 上下文爆炸 历史信息堆积超过窗口限制 多轮对话后丢失关键信息 偏离目标 执行过程中忘记原始任务 查询数据时被无关信息吸引

框架演进的长期目标

为了解决上述挑战,我们定义了框架长期发展的核心能力方向:

能力一:上下文管理

维度 当前状态 目标方式 消息管理 无管理,超出窗口直接溢出 裁剪 + 压缩 + Token预算控制 分层组装 全量消息平铺传递 分层注入:System Prompt → 记忆区 → 工具定义 → 对话历史 → Query 位置优化 无考虑 关键信息放高注意力区(开头20% + 结尾10%),避免中间丢失效应 最小上下文 全量历史堆积 子Agent隔离 + 相关性过滤,决策只依赖必要信息

能力二:工具/技能调度体系

问题 当前状态 目标方式 错误处理 报错返给LLM,无限重试相同错误 错误诊断 + 优雅降级 参数校验 完全信任LLM生成参数 Schema约束 + 类型检查 + 安全过滤 权限控制 无边界,可调用任意工具 工具白名单 + 操作审批流 + 审计日志

能力三:流程与约束管理

维度 当前状态 目标方式 流程决策 全靠LLM决定 增加人工介入节点,匹配固定规则 执行约束 无边界 工具白名单 + 操作审批流 审计追踪 日志零散 全链路可观测

能力四:外部化状态管理

状态类型 当前状态 目标状态 任务进度 内存中 持久化存储 流程节点 无记录 状态机追踪 恢复机制 无 断点恢复执行 审计日志 基础日志 完整操作记录

短期计划(近期待实现)

  • 🎯 Agent市场:动态注册和发现新Agent
  • 🧠 记忆增强:长期记忆 + 短期记忆分层
  • 🖼️ 多模态Agent:图像理解、语音交互

长期愿景(未来探索)

  • 🔄 Agent进化:基于反馈自动优化Prompt
  • 🔍 自我反思:执行结果自评与纠错
  • 📚 自主学习:从用户行为中学习新模式

总结:RuoYi-AI v3.0 实现了从”对话”到”行动”的关键跨越

核心原则

  • 渐进式演进:从简单场景开始迭代
  • 单一职责:每个Agent专注一件事
  • 工具驱动:通过Tools扩展能力边界
  • 可观测:全链路追踪确保可控

OpenClaw刷屏之后,GitHub上冒出了接近6000个相关仓库,Python、Rust、Go、C#都有实现——唯独Java一直是空白

直到JobRunr作者Ronald Dehuysser的新项目JavaClaw横空出世:基于Spring Boot + Spring AI + JobRunr的开源AI Agent框架,纯Java技术栈。

先说清楚:JavaClaw不是要取代OpenClaw。OpenClaw有25个传输通道、5400+技能、移动端应用,生态成熟度完全不在一个量级。JavaClaw更适合作为一个学习项目——代码量可控、架构清晰、用到的都是Java开发者熟悉的技术栈(Spring Boot、Advisor链、@Tool注解),非常适合用来理解一个AI Agent底层到底是怎么运转的。

在读源码之前,先交代一个有用的概念框架。Mitchell Hashimoto(HashiCorp联合创始人)提过一个说法叫Harness Engineering,核心公式是:

$\( ext{Agent} = ext{Model} + ext{Harness}\)$

你不是模型,那你就是Harness。

  • Model是能力的来源
  • Harness是模型之外的一切——系统提示词、工具调用、文件系统、编排逻辑、反馈回路、约束机制

模型本身只是”大脑”,只有Harness把状态、工具、反馈串起来,它才真正变成一个Agent。可以这么理解:模型是CPU,Harness是操作系统。CPU再强,OS拉胯也白搭。

用这个视角看JavaClaw,你会发现它本质上就是一套用Java/Spring Boot实现的Harness。下面我们逐层拆解。

JavaClaw的项目结构很清晰:

JavaClaw/ ├── base/ # 核心:Agent、任务、工具、频道、配置、记忆 ├── app/ # Spring Boot 入口、Onboarding 引导、Web 聊天频道 ├── plugins/ # 扩展插件:Telegram、Discord、Brave 搜索、Playwright 浏览器自动化 └── providers/ # LLM 提供商适配:OpenAI、Anthropic、Ollama、Google

四个模块各有明确的边界:

  • base是核心逻辑
  • app负责把base的能力暴露给用户
  • pluginsproviders则以可插拔模块的形式扩展通道、工具和模型能力

技术栈:Java 25 + Spring Boot 4.0.3 + Spring AI 2.0.0-SNAPSHOT + JobRunr 8.5.1

值得注意的技术选型:项目确实引入了Spring Modulith。不过从当前仓库能直接看到的内容看,它更像是在为模块化治理打基础,而不是已经把所有模块边界都用显式声明和验证测试铺满了。

这是全文最重要的部分。理解了Agent Loop,JavaClaw的其他模块就都通了。

Agent接口为什么只有两个方法

先看Agent接口的定义:

public interface Agent {

String respondTo(String conversationId, String question); 
    
    
      
        T prompt(String conversationId, String input, Class 
       
         result); 
        
      

}

就这么点东西。

  • 第一个方法处理对话——用户发消息,Agent回复文本
  • 第二个方法处理结构化输出——给Agent一个输入,让它返回指定类型的Java对象

接口越薄,实现越灵活。DefaultAgent的实现本质上是Spring AI ChatClient的薄封装。

看起来简单,但重点在chatClient的构建配置里——那里藏着一整条Advisor链,正是这条链实现了Agent的核心能力。

ChatClient管道拆解

JavaClaw在构建ChatClient时注册了一条Advisor链:

chatClientBuilder

.defaultAdvisors(new SimpleLoggerAdvisor()) .defaultSystem(p -> p.text(agentPrompt) .param(ENVIRONMENT_INFO_KEY, AgentEnvironment.info())) .defaultToolCallbacks(mcpToolProvider.getToolCallbacks()) .defaultToolCallbacks(SkillsTool.builder() .addSkillsDirectory(skillsDir(workspace).toString()).build()) .defaultTools(taskTool, checkListTool, mcpTool, shellTools, fileSystemTools, smartWebFetchTool) .defaultAdvisors( ToolCallAdvisor.builder().build(), MessageChatMemoryAdvisor.builder(chatMemory).build());

这条链的执行顺序是关键

SimpleLoggerAdvisor(最外层) ↓ 记录请求和响应日志,纯观察者角色 

ToolCallAdvisor(中间层)

↓ 这就是 ReAct 循环的实现者 

MessageChatMemoryAdvisor(最内层)

↓ 调用前加载历史消息,调用后保存本轮对话

一句话概括

  • MessageChatMemoryAdvisor管"记得住"
  • ToolCallAdvisor管"干得了"
  • SimpleLoggerAdvisor管"看得见"

ToolCallAdvisor如何驱动ReAct循环

ReAct(Reasoning and Acting)由Shunyu Yao等人于2022年在论文《ReAct: Synergizing Reasoning and Acting in Language Models》中提出,是当前AI Agent最主流的执行范式。思路很简单:

  1. 观察(Observe):把用户消息 + 历史对话 + 系统提示词 + 可用工具列表喂给LLM
  2. 思考(Think):LLM决定是直接回答,还是调用某个工具
  3. 行动(Act):如果LLM返回了工具调用请求,执行对应工具,把结果喂回LLM
  4. 循环:重复2-3,直到LLM认为不需要再调用工具,返回最终文本

在JavaClaw中,这个过程完全由Spring AI的ToolCallAdvisor驱动。你不需要自己写循环判断逻辑——ToolCallAdvisor拦截LLM的响应,如果包含tool call,就自动执行对应的@Tool方法,把结果拼回上下文,再调一次LLM。如此往复,直到LLM返回纯文本。

换句话说,JavaClaw的Agent循环没有手写while(true),靠的是Advisor链的拦截-转发机制。这是一个很"Spring"的做法——用声明式的管道代替命令式的循环。

ToolCallAdvisor内部做了几个关键操作:

  1. 通过setInternalToolExecutionEnabled(false)禁用模型内置的工具执行,由Advisor自己接管整个工具调用流程
  2. 递归调用一条复制的子链(callAdvisorChain.copy(this))来实现循环,直到响应中不再包含tool call
  3. 支持returnDirect机制——如果某个工具标记了returnDirect=true,Advisor直接把工具的返回值透传给调用方,跳过后续的模型处理

还有一个容易忽略的细节:Spring AI的ToolCallAdvisor支持两种对话历史模式。默认的conversationHistoryEnabled模式会在每次循环迭代中维护完整历史;如果配合了MessageChatMemoryAdvisor(JavaClaw就是这种情况),则会切换到disableMemory()模式,每次迭代只传递上一步的工具返回值,由Memory Advisor统一管理历史。避免同一条消息在Advisor链和Memory Advisor中被重复注入。

系统提示词的动态构建

系统提示词不是写死的。每次调用LLM时,系统提示由几部分动态拼接:

  1. AGENT.private.md:如果存在,优先作为私有系统提示词使用
  2. AGENT.md:当AGENT.private.md不存在时的回退提示词
  3. INFO.md:环境与工作区说明,拼接在Agent提示词后面
  4. AgentEnvironment.info():作为模板参数注入INFO.md里的{ENVIRONMENT_INFO}占位符

这意味着运行时环境信息不是简单粗暴地硬编码进系统提示,而是通过模板参数注入。这样Agent能知道"我现在在哪台机器上、几点了、工作目录和git状态如何",同时又保留了提示词文件本身的可编辑性。

Agent再强,也得有个入口接收用户消息、有个出口返回执行结果。这就是Channel系统的职责。

接口设计:两个方法解决所有问题

public interface Channel void sendMessage(String message); 

}

  • getName()用于标识频道
  • sendMessage()用于向用户推送消息

就这么简单。一共有三个实现类:WebSocket Chat、Telegram、Discord。

你可能会问:接收消息的方法呢?

答案是:每个Channel的接收逻辑由各自的传输协议决定(WebSocket handler、Telegram long-polling、Discord listener),不需要统一接口。Channel接口只管”出站”——把Agent的回复推给用户。

事件驱动的消息分发

每个Channel在收到用户消息后,做的事情是一样的:

  1. 把消息包装成ChannelMessageReceivedEvent发布出去
  2. 调用agent.respondTo(conversationId, message)
  3. 把Agent的回复通过sendMessage()推回用户

ChannelRegistry是所有频道的注册中心。它做了两件事:管理所有Channel实例,以及追踪最近一次消息事件(这样后台任务完成后知道往哪个频道发通知)。

Chat频道的双模式设计

内置的Chat频道(Web UI)用了WebSocket + REST双模式:

  • 优先走WebSocket实时推送,用户体验最好
  • 如果WebSocket会话断了,自动降级到内存队列,前端通过REST轮询拉取

这个降级策略用ConcurrentLinkedQueue实现,简单但有效。不需要引入消息队列中间件,对个人助手场景来说足够了。

插件式扩展

Telegram、Discord这些频道都是插件,通过Spring Boot的@AutoConfiguration + @ConditionalOnProperty实现条件激活:

@AutoConfiguration @ConditionalOnProperty(prefix = "agent.channels.telegram",

name = {"token", "username"}) 

public class TelegramChannelAutoConfiguration { … }

配置了Telegram的token和username才会创建Telegram频道Bean,否则完全不加载。这种”配置即插拔”的方式,是Spring生态的标准玩法,也是JavaClaw扩展性好的原因之一。

Agent能对话是基本功,能管理任务才是从聊天机器人到Agent的质变。

为什么用文件而不是数据库

JavaClaw的任务持久化方案很”朴素”——直接写Markdown文件。每个任务是一个.md文件,带YAML frontmatter:

— task: 调研 Spring AI 最新版本特性 createdAt: 2026-04-10T10:00:00Z status: todo description: 阅读 Spring AI 2.0 release notes,总结关键变化 —

文件命名按日期分桶,格式大致是:workspace/tasks/yyyy-MM-dd/HHmmss- .md

这里要注意两点:

  1. 状态不在文件名里,而是放在YAML frontmatter中
  2. 任务名会经过sanitize,中文和空格等字符不会原样出现在最终文件名里

这个选择背后有明确的权衡:

维度 文件持久化 数据库 可读性 直接打开看,人机两读 需要SQL查询或管理界面 运维复杂度 零依赖,目录就是数据 需要维护数据库实例 并发安全 文件锁,不适合高并发 天然支持 适用场景 个人助手,单用户 多用户,生产系统

对于个人AI助手来说,文件方案的优势很明显:你可以直接用文本编辑器查看、修改、甚至手动创建任务,Agent也能读写同样的文件。人和Agent共享同一份任务视图,这比任何数据库管理界面都直观。

任务状态机

任务有四个状态:

todo → in_progress → completed

 → awaiting_human_input

awaiting_human_input是个值得单独说的状态。Agent在执行任务时,可能遇到需要用户确认或补充信息的情况。这时候它不会猜,而是把状态设为awaiting_human_input,并通过当前活跃的Channel通知用户。

不过按当前代码实现,这条链路只做到"通知用户",还没有完整打通"用户后续回复自动重新挂回这条任务继续执行"的恢复机制;源码里甚至留着关于conversationId丢失的TODO。也正因为如此,这个状态更像是一个明确的暂停点,而不是已经闭环的人机协作工作流。

TaskManager与TaskHandler的分工

TaskManager是任务管理的编排器。它负责:

  • 创建任务文件
  • 把任务交给JobRunr调度(立即、延迟、或Cron定时)
  • 管理定时任务的增删

TaskHandler是实际干活的JobRunr作业处理器。它的executeTask()方法做了这几件事:

@Job(name = "%0", retries = 3) 

public void executeTask(String taskId) {

// 1. 加载任务,校验状态必须是 todo // 2. 更新状态为 in_progress // 3. 构造提示词,调用Agent(结构化输出) TaskResult result = agent.prompt(taskId, "Handle the following task...", TaskResult.class); // 4. 根据返回结果更新任务状态 // 5. 通过活跃 Channel 通知用户 // 异常时状态回退到 todo,JobRunr 自动重试 

}

注意第3步——这里用的不是respondTo()而是prompt(),要求LLM返回结构化的TaskResult对象,包含新状态和反馈文本。让LLM自己决定任务执行到什么程度,用结构化输出确保结果可解析。这比用正则表达式从自由文本中提取状态靠谱得多。

JobRunr的三种调度模式

模式 方法 用途 立即执行 jobScheduler.enqueue() “现在就做” 定时执行 jobScheduler.schedule() “下午3点做” 循环执行 jobRunr.scheduleRecurrently() “每天早上9点做”

JobRunr还自带一个Dashboard(端口8081),可以实时查看任务执行状态。对调试Agent行为来说,这个Dashboard比翻日志方便太多。

Agent光有脑子不够,还得有手有脚——能执行Shell命令、读写文件、搜索网页。这就是工具系统。

@Tool注解驱动的工具注册

JavaClaw的大部分自研工具都通过Spring AI的@Tool注解声明。比如TaskTool的方法:

@Tool(description = "Create a new task…") public String createTask(String name, String description) { … }

在ChatClient构建时统一注册。LLM在对话过程中决定调用哪个工具时,Spring AI负责匹配方法名、传递参数、执行方法、返回结果。开发者只需要写业务逻辑和加注解。

不过也不是所有能力都统一走@Tool这条路径。像SkillsTool和MCP ToolCallbacks,就是通过defaultToolCallbacks(…)注入到ChatClient里的。

内置工具全景

工具 能力 来源 ShellTools 执行bash命令(当前代码中默认未启用) spring-ai-community-agent-utils FileSystemTools 读写编辑文件 spring-ai-community-agent-utils SmartWebFetchTool 智能网页抓取(带LLM摘要) spring-ai-community-agent-utils TaskTool 创建、调度、管理任务 JavaClaw自研 CheckListTool 多步骤清单管理 JavaClaw自研 McpTool 动态注册MCP Server JavaClaw自研 SkillsTool 运行时加载技能 spring-ai-community-agent-utils

其中几个值得展开说说:

SmartWebFetchTool比普通的HTTP抓取工具多了一步——它内部会克隆一个新的ChatClient实例,把抓到的网页原文喂给LLM做摘要,只把摘要返回给Agent。这样做的好处是避免把几十KB的原始HTML直接灌进Agent的上下文窗口,节省Token且信息密度更高。

Playwright浏览器自动化(插件)提供了8个工具方法:navigateTo、clickElement、fillInput、getText、takeScreenshot、evaluateJavaScript、waitForSelector、closeBrowser。有两个实现细节值得注意:

  1. 返回的网页文本被截断到1万字符以内,防止撑爆上下文
  2. Playwright实例是懒初始化的,首次使用时才启动浏览器并自动安装Chromium

插件工具的自动发现

插件(Brave搜索、Playwright浏览器自动化)的工具注册用了AutoDiscoveredTool包装器:

record AutoDiscoveredTool 
    
    
      
        (T tool) { } 
      

插件把工具包装成这个record暴露为Spring Bean,ChatClient构建时自动收集所有AutoDiscoveredTool Bean并注册。新增插件工具不需要改核心代码,只需要加一个Bean。

Skills:零代码扩展

Skills系统可能是JavaClaw里最”魔法”的部分。你只需要在workspace/skills/下创建一个目录,放进一个SKILL.md文件,Agent就会自动发现并使用这个技能。

一句话定义:Skill是一个用自然语言定义的、具有特定领域上下文的逻辑指令集,本质上是通过延迟加载(Lazy Loading)优化Token消耗的子Agent。

SKILL.md通常包含两部分:

  1. YAML frontmatter(元数据,描述技能名称、触发条件等,始终加载)
  2. 正文(详细的自然语言指令,仅在触发时按需加载)

这个设计很关键——如果把所有Skill的完整内容都塞进系统提示词,几百个Skill会瞬间撑爆上下文窗口。延迟加载保证了Agent知道”有哪些技能可以用”(元数据常驻),但只在需要时才读取具体指令(正文按需注入)。

底层实现依赖spring-ai-community-agent-utils的SkillsTool。它在构建时扫描skills目录,把每个SKILL.md的元数据注册为工具回调。Agent在对话中判断需要某个技能时,调用SkillsTool读取对应的SKILL.md完整内容,将指令注入推理上下文,然后按照里面的规则行事。

这种”约定优于配置”的思路很像Spring Boot本身——不需要写Java代码,不需要编译部署,丢个文件进去就能扩展Agent能力。

Skills和MCP解决的是不同层面的问题

  • MCP负责把外部系统接入进来(连通性)
  • Skills负责决定什么时候用、怎么组合这些能力(编排逻辑)

一个高级Skill的底层完全可以编排多个MCP工具的调用。

MCP(Model Context Protocol)是Anthropic于2024年提出的开放协议,被形象地称为“AI领域的USB-C接口”——通过JSON-RPC 2.0统一了LLM与外部数据源/工具的通信规范。一次开发的MCP Server,所有支持MCP的AI应用都能直接复用。

MCP的架构分四层

组件 职责 MCP Host 运行AI应用,托管LLM(如JavaClaw本身) MCP Client 与Server建立1:1连接,转发JSON-RPC请求 MCP Server 实现MCP协议,暴露Resources/Tools/Prompts等能力 Data Source 提供实际数据或执行操作(文件系统、数据库、外部API)

一个Host可以管理多个Client,每个Client对应一个Server,互不影响。

JavaClaw通过Spring AI MCP Client接入了这个生态,支持两种传输模式:

  • stdio:本地进程间通信,适合Agent调用本机工具
  • Streamable HTTP:远程HTTP连接,适合接入云端MCP服务

集成分两层:

配置层:默认情况下,这些连接信息会落到app/src/main/resources/application.private.yaml;主application.yaml只是通过spring.config.import去导入它。Spring AI的自动配置会为每个声明的Server创建MCP客户端,获取其暴露的工具列表,转换为Spring AI的ToolCallback。

运行时层:Agent可以通过McpTool动态注册新的MCP Server。注册信息通过ConfigurationManager持久化到配置文件。配置变更后,JavaClawApplicationMonitor会监听事件,关闭并重新启动Spring应用上下文,让新配置生效。

这个”配置变更→自动重启”的方案看着粗暴,但对个人助手来说合理——你不会频繁改配置,改一次重启几秒钟完全可以接受。比设计一套热加载机制简单太多。

回顾一下JavaClaw的几个代表性设计决策:

1. ReAct循环:Advisor链 vs 手写循环

JavaClaw选择完全依赖Spring AI的ToolCallAdvisor,而不是自己实现循环。

好处:代码量极少,升级Spring AI版本就能获得循环逻辑的改进

代价:对Spring AI的依赖很深,如果Advisor的行为不符合预期,调试起来不如自己写的循环直观

2. 文件持久化 vs 数据库

前面已经分析过。一句话总结:对单用户场景,文件系统的可读性优势远大于数据库的并发优势

3. Spring Modulith vs 单体

在个人助手这种规模的项目里引入模块化框架,初看有点”过度设计”。但考虑到项目定位是”面向Java社区的开源框架”,模块化边界对贡献者来说就是一份隐形的架构文档——哪些可以动,哪些不能动,编译器帮你守着。

4. Chat Memory的处理

JavaClaw没有直接照搬Spring AI默认实现,而是自己复制并微调了两块Chat Memory组件。

第一块是MessageChatMemoryAdvisor:核心改动不是一句”把HashSet换成LinkedHashSet”就能概括的,而是用LinkedHashSet做去重并保持顺序,同时确保SystemMessage仍然排在最前面。这样既避免重复注入历史消息,也尽量保持对话上下文的稳定顺序。

第二块是MessageWindowChatMemory:它把策略调整成”仓库存全量、读取时做窗口裁剪”,而不是简单把仓库里的旧消息截掉。这个改法对排查问题很友好,因为磁盘上能保留完整历史,推理时再取最近窗口。

项目还自定义了AppendableChatMemoryRepository接口,给仓储层补了appendAll()这个扩展点。但要实话实说:当前文件系统实现的appendAll()仍然是先读已有消息、拼接新消息、再全量写回YAML文件,所以这里更准确地说是”为增量追加留出了接口”,而不是已经彻底消除了全量重写。

对话历史本身持久化为YAML文件(workspace/conversations/chat-{conversationId}.yaml),每条消息带有时间戳和角色标记。这也是文件优于数据库的一个体现——你可以直接打开YAML文件查看对话记录,排查Agent为什么做了某个决策时非常方便。

这些”小补丁”说明一个事实:Spring AI虽然发展很快,但在Agent的生产细节上还有不少坑需要踩

既然JavaClaw号称”Java版OpenClaw”,两者的差距在哪?直接看对比:

维度 OpenClaw JavaClaw 语言 TypeScript / Node.js Java 25 / Spring Boot 4 架构 WebSocket Gateway 控制面 + RPC 模式的 Agent 运行时 Spring AI ChatClient + Advisor 链 + JobRunr 后台任务 传输通道 25+(WhatsApp、Telegram、Slack、Discord、飞书、微信等) 3(Chat/WebSocket、Telegram、Discord) Skills生态 ClawHub 注册表,5400+ 技能 本地 workspace/skills/ 目录 任务调度 自研 JobRunr(带 Dashboard) 浏览器自动化 内置 Playwright 插件 客户端 macOS 菜单栏应用 + iOS/Android 伴侣应用 Web UI 安全模型 Docker 沙箱 + Tailscale 远程访问 本地运行,暂无沙箱隔离

OpenClaw在生态成熟度上领先很多——25个传输通道、移动端应用、技能商店。JavaClaw目前更像是概念验证+社区孵化器,项目README里也明确说了:”这是一份面向Java社区的公开邀请,让我们一起构建Java AI Agent的未来。”

JavaClaw有一个OpenClaw没有的优势:对Java/Spring开发者的技术亲和度。如果你想基于AI Agent做二次开发,用JavaClaw你面对的是熟悉的Spring Boot自动配置、Bean注册、Advisor模式,学习曲线比从零学TypeScript要平缓得多。

JavaClaw的Anthropic提供商有个”彩蛋”功能:如果你在Onboarding时把API Key设为 ,它会自动读取你系统里已有的Claude Code OAuth凭证,不需要手动填Key。

具体实现是AnthropicClaudeCodeOAuthTokenExtractor——在macOS上从Keychain读取Claude Code-credentials,在Linux上读取~/.claude/.credentials.json。拿到token后,它把标准的x-api-key头替换成Authorization: Bearer {token},并注入Claude Code专属的beta头和系统提示前缀。

这个设计让已经装了Claude Code的开发者可以零配置上手JavaClaw,很贴心。

文章开头提到OpenClaw生态已经蔓延到多种编程语言,这里展开列一下各语言的代表项目,方便感兴趣的读者按语言去研究:

语言 代表项目 Stars 简介 TypeScript OpenClaw / NanoClaw 349k+ / 3.2k 原版OpenClaw(TypeScript/Node.js);NanoClaw是轻量版,单Node.js进程+容器化Agent Python CowAgent 43k Python生态的Agent实现 Rust IronClaw 11.6k NEAR AI出品,WASM沙箱+PostgreSQL后端+AES-256加密凭证库,注重安全和隐私 Rust OpenCrust 86 直接从OpenClaw重写,16MB单二进制,13MB空闲内存,3ms冷启动,自带OpenClaw迁移工具 Rust MicroClaw 638 受NanoClaw启发的Rust实现,SQLite存储+MCP支持+Docker沙箱 Go GoClaw 2.4k OpenClaw的Go重写,多租户隔离+5层安全模型+原生并发 Go Open-IM-Server 16.2k Go微服务架构的IM开源方案,标签包含OpenClaw,常被用作Agent的消息通道 C# OpenClaw Windows Node 436 官方维护的Windows伴侣套件,含系统托盘应用、共享库和PowerToys扩展 Nix OpenClaw Nix 637 OpenClaw的Nix/NixOS打包,支持声明式部署 Elixir GoodWizard 37 NanoClaw的Elixir克隆,利用OTP的并发优势

可以看到,TypeScript原版一家独大,Rust生态最为活跃(IronClaw、OpenCrust、MicroClaw三足鼎立),Go在IM和Agent框架两个方向都有布局,C#有官方的Windows原生支持,Nix适合追求声明式部署的运维同学,Elixir则在探索OTP并发模型与Agent的结合。Java在这个版图里长期缺席,JavaClaw是目前第一个填补这个空白的项目


从源码里能看到几个有意思的结论:

Java生态完全有能力构建Agent
→ Spring AI的ChatClient + Advisor链 + @Tool注解,这套组合拳让Java开发者用熟悉的编程模型就能接入LLM能力



文件可以比数据库更好用
→ 在个人助手场景下,人和AI共享同一份文件视图,比任何管理界面都直观



Spring Boot的插件化机制天然适合Agent扩展
→ @AutoConfiguration + @ConditionalOnProperty,新增一个传输通道或工具,只需要加一个模块,核心代码零改动



  • RuoYi-AI:面向企业级应用,强调多Agent协作和工作流编排
  • JavaClaw:面向个人助手和学习研究,强调架构清晰度和学习价值

如果你想在Java技术栈上构建自己的AI Agent:

  • 关注RuoYi-AI的企业级落地实践,特别是SupervisorAgent的多Agent协作模式
  • 研究JavaClaw的源码实现,理解Spring AI Advisor链的工作原理
  • 项目地址:https://github.com/jobrunr/JavaClaw

让我们一起构建Java AI Agent的未来!🚀


参考资源

  • RuoYi-AI官方文档
  • JavaClaw GitHub仓库:https://github.com/jobrunr/JavaClaw
  • Spring AI官方文档
  • ReAct论文:https://arxiv.org/abs/2210.03629
  • AI应用开发实战和面试指南:javaguide.cn

本文基于RuoYi-AI v3.0正式版和JavaClaw最新源码编写,旨在帮助Java开发者快速入门AI Agent开发。如有问题欢迎讨论交流!

本文由mdnice多平台发布

小讯
上一篇 2026-04-16 13:12
下一篇 2026-04-16 13:10

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/262644.html