一个用Spring生态打造的本地化AI助手:开源项目JavaClaw的完整解析
过去两年,AI Agent领域的聚光灯几乎都打在Python生态上。LangChain、AutoGPT、OpenClaw……这些明星项目让Python开发者如鱼得水。
但Java开发者呢?
很多人默认:要做AI,必须转Python。
JavaClaw项目告诉我们:不用转。
它的核心理念很直白:构建AI Agent不需要发明新轮子。Java生态里早已有所有需要的零件:Spring AI(LLM集成)、JobRunr(可靠任务调度)、Spring Events(多端消息路由)、Spring Modulith(模块化架构)。你只需要像“连接点”一样把它们拼起来。
这就是JavaClaw(原名ClawRunr)的诞生背景:一个纯Java写成的、运行在你自己设备上的AI Agent。
它已经能做到:
-
在Telegram或浏览器里跟它聊天(多端支持) -
让它“每天上午9点整理今天待办”(可靠调度 + 自动重试) -
教它新技能:往 workspace/skills/丢一个SKILL.md文件,即刻生效 -
连接MCP工具,读写文件,执行Shell命令——数据全部留在本地
发布不到一周,600+ GitHub Star,社区已经自发写了插件。
这说明什么?Java开发者对“本地优先、可扩展AI Agent”的需求,一直都在。我们只是缺一个把零件拼起来的示范。
构建AI Agent必须解决的4个核心问题
在深入代码之前,我们先建立全局认知。一个真正的AI Agent,远不止是“把用户问题转发给LLM”。它至少需要解决以下4个核心问题:
|
|
|
|
|
|---|---|---|---|
| 1. 如何让Agent“开口说话”? |
|
Spring AI | DefaultAgent
ChatClient) |
| 2. 如何让Agent“耳听八方”? |
|
Spring Events | Channel
ChannelMessageReceivedEvent事件驱动 |
| 3. 如何让Agent“靠得住”? |
|
JobRunr | @Job(retries = 3)
|
| 4. 如何让Agent“长得大”? |
|
Spring Modulith | base
app → plugins模块化分层 |
整体架构速览
JavaClaw的项目结构本身就对应了这4个问题的答案:
JavaClaw/├── base/ # 核心模块:Agent、Tasks、Tools、Channels、Config│ → 解决“如何让Agent工作”(问题1、2、3的核心接口)├── app/ # 应用模块:Spring Boot入口、Onboarding、Chat UI│ → 解决“如何让用户用起来”└── plugins/ # 插件模块:Telegram、Discord等具体实现│ → 解决“如何长得大”(问题4)
核心组件一:Spring AI,让Agent“开口说话”
让Agent具备“智能”的第一步,当然是能调用LLM。JavaClaw没有自己封装OpenAI/Anthropic API,而是直接用了Spring AI——Spring生态的LLM抽象层。
20行代码的Agent核心
@ComponentpublicclassDefaultAgentimplementsAgent{privatefinal ChatClient chatClient;publicDefaultAgent(ChatClient chatClient){this.chatClient = chatClient; }@Overridepublic String respondTo(String conversationId, String question){return chatClient .prompt(question) .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId)) .call() .content(); }}
关键设计:
-
Provider无关: ChatClient是Spring AI的抽象,改一行配置就能从OpenAI切到Ollama -
对话记忆内置: ChatMemory自动管理上下文 -
极简抽象:Agent只暴露一个 respondTo()方法
提示词管理:用户可编辑的System Prompt
String agentPrompt = workspace.createRelative("AGENT.md") .getContentAsString(StandardCharsets.UTF_8) + workspace.createRelative("INFO.md") .getContentAsString(StandardCharsets.UTF_8);
-
AGENT.md:系统指令(角色、行为准则) -
INFO.md:环境上下文(自动注入,如当前时间)
用户修改文件即可定制Agent行为,无需重启。
工具注册:让Agent“有手有脚”
chatClientBuilder .defaultSystem(p -> p.text(agentPrompt)) .defaultToolCallbacks(mcpToolProvider.getToolCallbacks()) .defaultToolCallbacks(SkillsTool.builder() .addSkillsDirectory(skillsDir.toString()).build()) .defaultTools( TaskTool.builder().taskManager(taskManager).build(), ShellTools.builder().build(), FileSystemTools.builder().build(), SmartWebFetchTool.builder(...).build() )
@Tool注解的方法会被Spring AI自动发现,LLM根据对话决定何时调用。
核心组件二:Spring Events,让Agent“耳听八方”
一个AI Agent只支持单一聊天界面显然不够用。JavaClaw用Spring Events实现了一套优雅的多端抽象。
Channel接口:统一所有通信渠道
publicinterfaceChannel{default String getName(){return getClass().getSimpleName(); }voidsendMessage(String message);}
事件驱动:解耦消息接收和处理
// 收到消息时发布事件applicationEventPublisher.publishEvent(new ChannelMessageReceivedEvent("telegram", userMessage, chatId));
ChannelRegistry:记住“从哪来”,回哪去
@ServicepublicclassChannelRegistry{public Channel getLatestChannel(){// 返回最后活跃的Channel,用于响应原路返回 }}
工作流程:
-
Telegram收到消息 → 发布 ChannelMessageReceivedEvent -
事件被捕获 → 记录“最后活跃渠道” -
Agent处理请求(不关心消息来源) -
响应 → 从最后活跃渠道发送回去
Agent核心代码完全不知道消息来自哪里。
核心组件三:JobRunr,让Agent“靠得住”
如果说Spring AI让Agent“有脑子”,Spring Events让Agent“有耳朵”,那么JobRunr让Agent“靠得住”。
TaskHandler:一个注解搞定重试和持久化
@Job(name = "%0", retries = 3) // 核心:自动重试3次publicvoidexecuteTask(String taskId){ Task task = taskRepository.getTaskById(taskId);// ... 执行逻辑// 异常时抛出,JobRunr自动重试}
JobRunr带来的能力:
-
持久化:任务存入数据库,重启不丢失 -
重试:指数退避策略,最多3次 -
仪表盘:内置Web UI( localhost:8081),可视化监控 -
分布式:支持集群部署
三种任务创建方式
// 立即执行taskManager.create("整理笔记", "把临时笔记整理到归档文件夹");// 延迟执行taskManager.schedule(LocalDateTime.now().plusHours(1), "提醒开会", "下午2点有技术评审");// 周期执行(Cron)taskManager.scheduleRecurrently("0 0 9 * * *", "每日站会提醒", "每天早上9点发站会通知");
与通用方案的对比
|
|
|
|
|
|
|---|---|---|---|---|
@Scheduled
@Async |
|
|
|
|
|
|
|
|
|
|
| JobRunr |
|
|
|
|
核心组件四:Spring Modulith,让Agent“长得大”
一个AI Agent要想有生命力,必须能被社区轻松扩展。JavaClaw用Spring Modulith来保障模块边界清晰。
模块化的项目结构
JavaClaw/├── base/ # 定义接口和抽象├── app/ # 组装模块,提供运行入口└── plugins/ # 具体实现(Telegram、Discord等)
依赖方向单向:app → base ← plugins
社区扩展案例
作者提到:*”发布三天内,有人写了一个把Bot消息流式传输到Web界面的插件。他们不需要修改Agent核心代码,只需要一个新模块,实现对应的接口。”*
动手跑起来:快速开始指南
克隆与启动
git clone https://github.com/jobrunr/javaclaw.gitcd javaclaw./gradlew :app:bootRun
-
主应用端口: 8080 -
JobRunr Dashboard端口: 8081
Onboarding向导
访问 http://localhost:8080/onboarding,7步配置:
-
选择LLM Provider(OpenAI/Anthropic/Ollama) -
填写API Key -
定制Agent Prompt(编辑 workspace/AGENT.md) -
可选配置MCP服务器、Telegram等 -
完成
首次对话验证
访问 http://localhost:8080/chat,输入消息:
你好,请帮我列出当前目录下的文件
Agent应该会调用FileSystemTools,返回目录列表。
进阶与调优
动态工具发现
当工具有几十个,提示词过长、LLM容易选错时,启用动态发现:
javaclaw:tools:dynamic-discovery:enabled:truemax-results:8lucene-min-score-threshold:0.25
Skills:运行时添加新能力
创建workspace/skills/groceries/SKILL.md:
# 购物清单管理技能## 能力描述你可以用这个技能管理用户的购物清单## 操作指南- 查看清单:读取 workspace/lists/groceries.md- 添加物品:追加到文件末尾
Agent会在运行时自动加载,不需要重启。
安全边界提醒
建议在AGENT.md中加入安全约束:
## 安全约束1. 执行Shell命令前,先列出命令内容并请求用户确认2. 不要读取`/etc/`、`/root/`等系统目录3. 删除文件前,先输出文件内容请求确认
写在最后
-
不要被“AI Agent必须用Python”的观念限制 -
善用Spring生态,它们不是为AI设计的,但恰好解决了Agent的需求 -
从JavaClaw开始,但不止于JavaClaw
如果本文帮你打开了Java+AI的思路,欢迎收藏和分享。
你也在用Java构建AI应用吗?欢迎在留言区分享你的经验。
夜雨聆风