乐于分享
好东西不私藏

Java生态的OpenClaw

Java生态的OpenClaw

概述

如今 AI 智能体已然遍地开花,但绝大多数都基于 Python 或 Node.js 开发。作为 Java 开发者,你或许一直置身局外观望,好奇 Java 生态何时才能迎头赶上。

事实上,JavaClaw 是一款基于 Java 生态构建的本地化个人 AI 智能体平台,其核心定位是:在用户自有硬件上运行,充当多渠道 AI 智能体的控制平面(Control Plane)。它不仅是一个对话机器人,更是一个具备任务调度、工具调用、多渠道接入、记忆管理能力的全功能智能体运行时。

从官方的描述来看,JavaClaw 最初作为 JobRunr 的演示项目诞生,但已发展成为面向 Java 社区的开放式 AI 智能体基础框架,致力于让开发者能以 Java 为母语构建下一代 AI 智能体系统。

项目口号: "让数据留在本地,让能力延伸到无限。"

核心优势

隐私优先的本地化设计

与大多数 AI 助手依赖云端服务不同,JavaClaw 完全在用户本地运行。对话记忆以 YAML 文件形式落盘,任务以 Markdown 文件形式存储,无需外部数据库,所有敏感数据均不出本机(除非用户主动配置 OpenAI/Anthropic 等外部 LLM)。

深度拥抱 Java 生态

JavaClaw 选用当下最先进的 Java 技术栈:Java 25、Spring Boot 4.0.3、Spring AI 2.0.0,完全依托 JVM 生态,无需 Python 环境,开发者可用熟悉的 Spring 开发范式扩展智能体能力。

多 LLM 后端可插拔

通过 Spring AI 的抽象层,JavaClaw 可无缝切换 OpenAI、Anthropic Claude 或本地 Ollama 模型,不需要修改业务代码,只需在 application.yaml 中更换配置项即可。

多渠道统一接入

支持 Web Chat(WebSocket)、Telegram、Discord 三种开箱即用的通信渠道,所有渠道共享同一个智能体内核和对话记忆,用户可以从 Telegram 发起任务,在 Discord 查看进度,体验真正的全渠道智能体。

运行时可扩展的 Skills 机制

通过在 workspace/skills/ 目录下投放 SKILL.md 文件,智能体在无需重启、无需修改代码的情况下即可习得新技能,这是 JavaClaw 最具差异化的能力之一。

企业级后台任务调度

基于 JobRunr 提供一次性、延迟、Cron 周期性三种任务调度模式,并内置可视化 Dashboard(:8081),任务状态实时可查,具备生产级可靠性。

MCP 协议支持

完整实现了 Model Context Protocol(MCP)客户端,支持 stdio 和 streamable-HTTP 两种模式,可无缝接入任何标准 MCP 工具服务器,大幅扩展工具调用能力边界。

架构

整体架构总览

JavaClaw 采用模块化单体(Modular Monolith)架构,借助 Spring Modulith 对模块边界进行约束,在保留单体部署便利性的同时,实现模块间的低耦合。

LLM 后端
存储层 Storage Layer
任务调度层 Task Layer
工具层 Tool Layer
Advisor 链
智能体核心层 Agent Core
通信渠道层 Channel Layer
配置与引导层 Config Layer
ConfigurationManager
application.yaml
OnboardingController
Web ChatWebSocket/REST
TelegramLong Polling
DiscordJDA Gateway
ChannelRegistry渠道注册中心
DefaultAgent智能体实现
ChatClientSpring AI
SimpleLoggerAdvisor日志
ToolCallAdvisor工具调用
MessageChatMemoryAdvisor记忆
ToolSearchAdvisor动态发现可选
TaskTool任务管理
CheckListTool清单管理
FileSystemTools文件操作
SmartWebFetchTool智能抓取
SkillsTool技能加载
McpToolMCP注册
AutoDiscoveredTool自动发现
TaskManager
JobRunr后台调度
TaskHandler
RecurringTaskHandler
FileSystem Memory对话记忆YAML
FileSystemTaskRepository任务Markdown
H2 DatabaseJobRunr元数据
WorkspaceAGENT.md/skills/context
OpenAI API
Anthropic API
Ollama Local
MCP Servers

模块结构划分

Gradle Multi-Project
JavaClaw Root
:base核心框架
:app启动入口
:plugins:brave搜索插件
:plugins:discordDiscord渠道
:plugins:telegramTelegram渠道
:plugins:playwright浏览器自动化

Spring Modulith 内部模块边界

base 模块内部结构
agentAgent / DefaultAgent
JavaClawConfigurationChatClient装配
toolsTaskTool / CheckListToolMcpTool / AutoDiscoveredTool
tasksTaskManager / TaskHandlerTaskRepository
configurationConfigurationManager
channelsChannel / ChannelRegistry
onboardingOnboardingProvider

技术栈

层次
技术选型
版本
运行时
Java
25
应用框架
Spring Boot
4.0.3
模块化
Spring Modulith
2.0.3
AI 抽象
Spring AI
2.0.0-SNAPSHOT
任务调度
JobRunr
8.5.0
内嵌数据库
H2(文件模式)
—
前端
htmx + Bulma
2.0.8 / 1.0.4
模板引擎
Pebble
4.1.1
Telegram SDK
TelegramBots
9.4.0
Discord SDK
JDA
6.1.1

Agent 运行原理

核心抽象:Agent 接口

Agent 接口是整个智能体的行为契约,定义了两种基本能力:

publicinterfaceAgent {// 自然语言对话:给定会话ID和用户问题,返回智能体回复    String respondTo(String conversationId, String question);// 结构化输出:给定会话ID和提示词,将回复解析为指定Java类型    <T> T prompt(String conversationId, String input, Class<T> result);}

DefaultAgent 是其唯一实现,核心逻辑极为简洁——将调用完全委托给 Spring AI 的 ChatClient,真正的复杂性被封装在 Advisor 链中。

ChatClient 的构建与装配

JavaClawConfiguration 是整个系统的"大脑装配车间",它在应用启动时构建出一个功能完备的 ChatClient:

@Bean@DependsOn({"mcpHeaderCustomizer"})public ChatClient chatClient(        ChatClient.Builder chatClientBuilder,        ChatMemory chatMemory,        ObjectProvider<ToolSearchToolCallAdvisor> toolSearchAdvisorProvider,        SyncMcpToolCallbackProvider mcpToolProvider,        TaskManager taskManager,        ConfigurationManager configurationManager,@Value("${agent.workspace:Unknown}") Resource workspace,        Set<AutoDiscoveredTool<?>> autoDiscoveredTools)throws IOException {// 1. 加载系统提示词(AGENT.md + INFO.md)StringagentPrompt= agentMd.getContentAsString(UTF_8)                       + infoMd.getContentAsString(UTF_8);// 2. 决定工具调用模式:动态发现 or 全量暴露ToolCallAdvisortoolCallAdvisor=        toolSearchAdvisorProvider.getIfAvailable(ToolCallAdvisor.builder()::build);// 3. 注册所有内置工具    chatClientBuilder        .defaultSystem(p -> p.text(agentPrompt)                             .param(ENVIRONMENT_INFO_KEY, AgentEnvironment.info()))        .defaultToolCallbacks(mcpToolProvider.getToolCallbacks())        .defaultToolCallbacks(SkillsTool.builder()                               .addSkillsDirectory(skillsDir(workspace)).build())        .defaultTools(            TaskTool.builder().taskManager(taskManager).build(),            CheckListTool.builder().build(),            McpTool.builder().configurationManager(configurationManager).build(),            FileSystemTools.builder().build(),            SmartWebFetchTool.builder(chatClientBuilder.clone().build()).build())        .defaultAdvisors(newSimpleLoggerAdvisor(),            toolCallAdvisor,                                    // 工具调用            MessageChatMemoryAdvisor.builder(chatMemory).build()); // 记忆注入// 4. 自动发现并注册插件工具    autoDiscoveredTools.forEach(t -> chatClientBuilder.defaultTools(t.tool()));return chatClientBuilder.build();}

完整的消息处理流程

Tool(TaskTool/FileSystem等)LLM(OpenAI/Anthropic/Ollama)对话记忆FileSystemAdvisor 链ChatClientDefaultAgentChannelRegistryChannel(Telegram/Discord/WebChat)Tool(TaskTool/FileSystem等)LLM(OpenAI/Anthropic/Ollama)对话记忆FileSystemAdvisor 链ChatClientDefaultAgentChannelRegistryChannel(Telegram/Discord/WebChat)记录最近活跃渠道alt[LLM 决定调用工具][LLM 直接回复]用户发送消息publishMessageReceivedEvent(event)respondTo(conversationId, message)chatClient.prompt(message).advisors(conversationId).call()进入 Advisor 链SimpleLoggerAdvisor记录请求日志读取历史对话记录返回 N 条历史消息发送[系统提示+历史+当前消息]返回 ToolCall 请求ToolCallAdvisor 拦截执行工具调用返回工具结果携带工具结果重新请求返回最终文本回复返回文本回复将本轮对话写入记忆返回最终内容content()返回回复文本发送回复消息用户

Advisor 链的执行顺序与职责

JavaClaw 的 Advisor 链按以下顺序执行,每个 Advisor 都可以在请求到达 LLM 前后进行拦截和增强:

Advisor 链(按注册顺序)
用户请求
SimpleLoggerAdvisor① 记录请求/响应日志
ToolSearchToolCallAdvisor② 动态发现相关工具(可选,Lucene检索)
ToolCallAdvisor③ 拦截LLM工具调用循环执行直到无调用
MessageChatMemoryAdvisor④ 注入历史记忆控制上下文窗口大小
LLM API
智能体响应

动态工具发现机制

当 javaclaw.tools.dynamic-discovery.enabled=true 时,系统启用工具搜索模式(Tool Search Pattern):

全量工具池
用户消息
ToolSearchToolCallAdvisor
Lucene 本地索引对所有工具描述建立倒排索引
语义匹配Top-K 工具
仅将匹配工具注入上下文
LLM 请求Token 数量受控
FileSystemTools
TaskTool
SmartWebFetchTool
SkillsTool
MCP Tools...
N 个工具...

配置示例:

javaclaw:tools:dynamic-discovery:enabled:truemax-results:8# 最多注入 8 个工具lucene-min-score-threshold:0.25# 相关性最低分阈值

后台任务执行流程

后台任务是 JavaClaw 区别于普通聊天机器人的核心特性。任务的创建和执行形成了一个完整的异步闭环:

ChannelRegistryTaskHandlerJobRunr SchedulerFileSystemTaskRepositoryTaskManagerTaskToolAgent (LLM)ChannelRegistryTaskHandlerJobRunr SchedulerFileSystemTaskRepositoryTaskManagerTaskToolAgent (LLM)用户请求"明天9点提醒我开会"写入 workspace/tasks/yyyy-MM-dd/HHmmss-todo-name.md持久化到 H2 数据库到达执行时间Agent 执行任务,可能调用工具通过最近活跃渠道通知用户scheduleTask("2026-04-20T09:00:00", "morning-meeting", "...")schedule(localDateTime, name, desc)save(Task.newTask(...))jobScheduler.schedule(executionTime,x -> x.executeTask(taskId))executeTask(taskId)读取任务描述,更新状态 in_progressagent.respondTo(taskId, taskDescription)返回执行结果更新状态 completedgetLatestChannel().sendMessage(result)

对话记忆的持久化策略

JavaClaw 采用基于文件系统的对话记忆,对话历史存储为 YAML 文件,支持跨进程重启的记忆持久化:

存取

写入

读取窗口

注入

MessageChatMemoryAdvisor
FileSystemChatMemoryRepository
workspace/context/{conversationId}.yaml
MessageWindowChatMemory滑动窗口截断
LLM 上下文

Skills扩展

JavaClaw 提供了三种层次的扩展方式,从零代码到完整插件,覆盖不同的扩展场景。

扩展方式一:零代码 SKILL.md 技能文件

这是最轻量的扩展方式,无需修改任何代码,无需重启服务,适合快速为 Agent 添加新的知识、流程或行为规范。

工作原理:

SkillsTool 在每次被 LLM 调用时,动态扫描 workspace/skills/ 目录,将所有 SKILL.md 文件的内容作为上下文注入 Agent。

调用

扫描

加载内容

返回技能描述

LLM 决策
SkillsTool
workspace/skills/├── daily-report/│ └── SKILL.md├── code-review/│ └── SKILL.md└── meeting-notes/└── SKILL.md

示例:创建一个"每日周报"技能

在 workspace/skills/daily-report/SKILL.md 中写入:

# Daily Report Skill你可以帮助用户生成每日工作总结报告。## 使用场景当用户要求"生成今日报告"、"写工作总结"时,触发此技能。## 报告格式1.**今日完成工作**:列出当天已完成的任务2.**进行中的工作**:当前仍在处理的任务3.**明日计划**:下一个工作日的计划4.**遇到的问题**:任何阻塞项或需要协助的事项报告语言:中文,语气专业简洁。

文件放入目录后,Agent 立即具备生成标准化报告的能力,无需任何重启。

扩展方式二:实现 AutoDiscoveredTool 插件工具

当需要实现有状态的工具或调用外部 API 时,通过 Java 代码实现工具类,并注册为 AutoDiscoveredTool Bean,框架会在启动时自动发现并将其注入 ChatClient。

扩展步骤:

第一步:定义工具类

package com.example.tools;import org.springframework.ai.tool.annotation.Tool;import org.springframework.stereotype.Component;/** * 自定义天气查询工具示例 */@ComponentpublicclassWeatherTool {privatefinal WeatherApiClient weatherApiClient;publicWeatherTool(WeatherApiClient weatherApiClient) {this.weatherApiClient = weatherApiClient;    }@Tool(name = "getWeather",          description = "根据城市名称查询当前天气情况。返回温度、天气状况和湿度信息。")public String getWeather(String city) {try {WeatherDatadata= weatherApiClient.fetchWeather(city);return String.format("城市:%s,温度:%s°C,天气:%s,湿度:%s%%",                city, data.temperature(), data.condition(), data.humidity()            );        } catch (Exception e) {return"无法获取 " + city + " 的天气信息:" + e.getMessage();        }    }@Tool(name = "getWeatherForecast",          description = "获取指定城市未来 N 天的天气预报,N 最大为 7。")public String getWeatherForecast(String city, int days) {// 实现天气预报逻辑...return weatherApiClient.fetchForecast(city, Math.min(days, 7)).toString();    }}

第二步:注册为 AutoDiscoveredTool

package com.example.config;import ai.javaclaw.tools.AutoDiscoveredTool;import com.example.tools.WeatherTool;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@ConfigurationpublicclassWeatherToolConfiguration {/**     * 将 WeatherTool 包装为 AutoDiscoveredTool,     * JavaClawConfiguration 会自动发现并注册它。     */@Beanpublic AutoDiscoveredTool<WeatherTool> weatherAutoDiscoveredTool(WeatherTool weatherTool) {returnnewAutoDiscoveredTool<>(weatherTool);    }}

框架自动发现机制(源码):

在 JavaClawConfiguration.chatClient() 方法中,框架通过如下方式自动注册所有 AutoDiscoveredTool:

// 自动将所有 AutoDiscoveredTool Bean 注册到 ChatClientautoDiscoveredTools.forEach(autoDiscoveredTool ->    chatClientBuilder.defaultTools(autoDiscoveredTool.tool()));
JavaClawConfiguration
Spring 容器启动
WeatherTool Bean
WeatherToolConfiguration
AutoDiscoveredTool Bean
Set> autoDiscoveredToolsSpring 自动注入所有实现
autoDiscoveredTools.forEach(  t -> chatClientBuilder.defaultTools(t.tool()))
ChatClient已含 WeatherTool

扩展方式三:实现完整的 Channel 渠道插件

当需要接入新的消息渠道(如企业微信、飞书、SMS 等)时,实现 Channel 接口并将其注册到 ChannelRegistry。

扩展步骤:

第一步:实现 Channel 接口

package com.example.channels;import ai.javaclaw.channels.Channel;import ai.javaclaw.channels.ChannelRegistry;import ai.javaclaw.channels.ChannelMessageReceivedEvent;import ai.javaclaw.agent.Agent;import org.springframework.stereotype.Component;/** * 企业微信渠道插件示例 */@ComponentpublicclassWeChatWorkChannelimplementsChannel {privatestaticfinalStringCONVERSATION_ID="wechat-work-channel";privatefinal Agent agent;privatefinal ChannelRegistry channelRegistry;privatefinal WeChatWorkClient weChatClient;publicWeChatWorkChannel(Agent agent,                              ChannelRegistry channelRegistry,                              WeChatWorkClient weChatClient) {this.agent = agent;this.channelRegistry = channelRegistry;this.weChatClient = weChatClient;// 注册自身到渠道注册中心        channelRegistry.registerChannel(this);// 启动消息接收监听        startMessageListener();    }@Overridepublic String getName() {return"WeChatWorkChannel";    }@OverridepublicvoidsendMessage(String message) {// 通过企业微信 API 发送消息        weChatClient.sendTextMessage(message);    }privatevoidstartMessageListener() {// 企业微信消息回调处理        weChatClient.onMessageReceived(event -> {StringuserId= event.getUserId();Stringcontent= event.getContent();// 通知 ChannelRegistry 当前活跃渠道            channelRegistry.publishMessageReceivedEvent(newChannelMessageReceivedEvent(this.getName())            );// 转发给 Agent 处理,使用 userId 作为会话ID以隔离对话记忆Stringresponse= agent.respondTo(userId, content);// 回复用户            sendMessage(response);        });    }}

第二步:添加 Onboarding 步骤(可选)

实现 OnboardingProvider 接口,在引导流程中为用户提供渠道配置 UI:

package com.example.channels;import ai.javaclaw.onboarding.OnboardingProvider;import ai.javaclaw.configuration.ConfigurationManager;import org.springframework.stereotype.Component;import java.util.Map;@ConfigurationpublicclassWeChatWorkOnboardingProviderimplementsOnboardingProvider {privatefinal ConfigurationManager configurationManager;publicWeChatWorkOnboardingProvider(ConfigurationManager configurationManager) {this.configurationManager = configurationManager;    }@Overridepublic String getStepId() {return"wechat-work";    }@Overridepublic String getTitle() {return"企业微信配置";    }@OverridepublicbooleanisCompleted() {// 检查配置是否已完成return configurationManager.hasProperty("agent.channels.wechat-work.corp-id");    }publicvoidsaveConfig(String corpId, String agentId, String secret) {        configurationManager.updateProperties(Map.of("agent.channels.wechat-work.corp-id", corpId,"agent.channels.wechat-work.agent-id", agentId,"agent.channels.wechat-work.secret", secret        ));    }}

三种扩展方式对比

扩展方式
适用场景
是否需要重启
代码量
SKILL.md 文件
新增知识、行为规范、工作流指引
否
零代码
AutoDiscoveredTool
调用外部 API、有状态工具、复杂计算
是(首次)
少量
Channel 插件
接入新消息平台(企微、飞书等)
是
中等
MCP 服务器
接入标准 MCP 协议工具
是
零代码配置

MCP 服务器动态注册

JavaClaw 支持在运行时通过 McpTool 注册新的 MCP 服务器,这一操作可以直接通过自然语言对话触发:

// 用户对话示例:// "帮我添加一个 MCP 服务器,地址是 http://localhost:8000/mcp,名字叫 my-tools"// Agent 自动调用 McpTool.addStreamableHttpMcpServer()@Tool(description = "添加新的 streamable-HTTP MCP 服务器到应用配置中")public String addStreamableHttpMcpServer(String name, String url, String headers) {// 持久化到 application.private.yaml    configurationManager.updateProperties(Map.of("spring.ai.mcp.client.streamable-http.connections." + name + ".url", url    ));return"MCP 服务器已添加,重启后生效";}

总结

JavaClaw 以其"本地优先、Java 原生、极简扩展"的设计哲学,为 Java 开发者提供了一个生产级的 AI 智能体运行时平台。

在架构层面,它借助 Spring Modulith 实现模块化约束,以 Spring AI 的 ChatClient 和 Advisor 模式为核心,将记忆管理、工具调用、日志追踪等横切关注点优雅地解耦;在智能体运行层面,Advisor 链的洋葱式执行模型和动态工具发现机制使得 Agent 既高效又灵活;在可扩展性层面,从零代码的 SKILL.md 到插件式的 AutoDiscoveredTool,再到渠道级别的 Channel 扩展,形成了层次清晰、门槛渐进的完整扩展体系。

对于 Java 团队而言,JavaClaw 提供的不仅是一个可直接使用的个人助手,更是一套可以作为企业级 AI 智能体平台基础的参考架构,值得深入研究与实践。

相关学习资料