AI执行器集成OpenClaw: 新增“openClawJobHandler”内置AI任务,与OpenClaw集成打通,支持快速开发AI类任务。

3.4.0-release
xuxueli 3 months ago
parent 80b2df78f5
commit 1c40e456af

@ -1271,6 +1271,7 @@ public void demoJobHandler() throws Exception {
- 执行器代码xxl-job-executor-sample-springboot-ai
**执行器内置任务列表:**
- a、ollamaJobHandler OllamaChat任务支持自定义prompt、input等输入信息。示例任务入参如下
```
{
@ -1279,6 +1280,7 @@ public void demoJobHandler() throws Exception {
"model": "{模型实现如qwen3.5:2b可选信息}"
}
```
- b、difyWorkflowJobHandlerDifyWorkflow 任务支持自定义inputs、user、baseUrl、apiKey 等输入信息,示例参数如下;
```
{
@ -1291,6 +1293,14 @@ public void demoJobHandler() throws Exception {
}
```
- c、openClawJobHandler OpenClaw任务支持自定义prompt、input等输入信息。示例任务入参如下
```
{
"input": "{输入信息,必填信息}",
"prompt": "{模型prompt可选信息}"
}
```
- 依赖1参考 [Ollama本地化部署大模型](https://www.xuxueli.com/blog/?blog=./notebook/13-AI/%E4%BD%BF%E7%94%A8Ollama%E6%9C%AC%E5%9C%B0%E5%8C%96%E9%83%A8%E7%BD%B2DeepSeek.md) 执行器示例部署“qwen2.5:1.5b”模型,也可自定选择其他模型版本。
- 依赖2参考 [使用DeepSeek与Dify搭建AI助手](https://www.xuxueli.com/blog/?blog=./notebook/13-AI/%E4%BD%BF%E7%94%A8DeepSeek%E4%B8%8EDify%E6%90%AD%E5%BB%BAAI%E5%8A%A9%E6%89%8B.md)执行器示例新建Dify DifyWork应用并在开始节点添加“input”参数可结合实际情况调整。
- 依赖3启动示例 “AI执行器” 相关配置文件说明如下:
@ -2788,15 +2798,16 @@ public void execute() {
- 10、【优化】统一项目依赖管理结构依赖版本统一到父级pom提升可维护性
### 7.44 版本 v3.4.0 Release Notes[ING]
- 1、【新增】调度性能提升任务触发后分批批量更新高频调度场景可百倍降低SQL操作合并执行提升调度性能
- 1、【新增】AI执行器集成OpenClaw: 新增“openClawJobHandler”内置AI任务与OpenClaw集成打通支持快速开发AI类任务。
- 2、【新增】调度批次写聚合提升调度性能任务触发后分批批量更新高频调度场景可百倍降低SQL操作合并执行提升调度性能
任务触发后批量更新配置“xxl.job.schedule.batchsize”
- 2、【调整】固定频率调度策略调整修复小概率下触发时间偏差问题
- 3、【调整】Docker基础镜像调整为eclipse-temurin
- 4、【优化】父POM依赖配置优化移除容易配置合并PR-3926
- 5、【升级】升级多项maven依赖至较新版本
- 3、【调整】固定频率调度策略调整修复小概率下触发时间偏差问题
- 4、【调整】Docker基础镜像调整为eclipse-temurin
- 5、【优化】父POM依赖配置优化移除容易配置合并PR-3926
- 6、【优化】调度日志优化支持执行器维度查看调度日志新增调度日志索引提升查询效率
(数据库新增索引脚本:``` create index I_jobgroup on xxl_job_log (job_group); ```
- 7、【TODO】调度中心OpenAPI完善提供任务管理能力封装Agent Skill并推送ClawHub
- 7、【升级】升级多项maven依赖至较新版本
- 8、【TODO】调度中心OpenAPI完善提供任务管理能力封装Agent Skill并推送ClawHub
### TODO LIST

@ -157,7 +157,7 @@ INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_desc`, `add_time`, `update_ti
VALUES (1, 1, '示例任务01', now(), now(), 'XXL', '', 'CRON', '0 0 0 * * ? *',
'DO_NOTHING', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化',
now(), ''),
(2, 2, 'Ollama示例任务01', now(), now(), 'XXL', '', 'NONE', '',
(2, 2, 'Ollama示例任务', now(), now(), 'XXL', '', 'NONE', '',
'DO_NOTHING', 'FIRST', 'ollamaJobHandler', '{
"input": "慢SQL问题分析思路",
"prompt": "你是一个研发工程师,擅长解决技术类问题。",
@ -172,6 +172,12 @@ VALUES (1, 1, '示例任务01', now(), now(), 'XXL', '', 'CRON', '0 0 0 * * ? *'
"user": "xxl-job",
"baseUrl": "http://localhost/v1",
"apiKey": "app-OUVgNUOQRIMokfmuJvBJoUTN"
}', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE',
now(), ''),
(4, 2, 'OpenClaw示例任务', now(), now(), 'XXL', '', 'NONE', '',
'DO_NOTHING', 'FIRST', 'difyWorkflowJobHandler', '{
"input": "查看下上海今天得天气,给出出游建议",
"prompt": "你是一个出游助手,擅长做旅游规划"
}', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE',
now(), '');

@ -161,12 +161,17 @@
<version>${xxl-sso.version}</version>
</dependency>
<!-- spring-ai -->
<!-- spring-ai: ollama、openai -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<!-- dify -->
<dependency>
<groupId>io.github.imfangs</groupId>

@ -37,11 +37,16 @@
<artifactId>xxl-job-core</artifactId>
</dependency>
<!-- spring-ai -->
<!-- spring-ai: ollama -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<!-- spring-ai: openai -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- dify -->
<dependency>
<groupId>io.github.imfangs</groupId>

@ -15,6 +15,7 @@ import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaChatOptions;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@ -32,6 +33,8 @@ public class AIXxlJob {
@Resource
private OllamaChatModel ollamaChatModel;
@Resource
private OpenAiChatModel openAiChatModel;
/**
* 1ollama Chat
@ -252,4 +255,91 @@ public class AIXxlJob {
}
// --------------------------------- openclaw ---------------------------------
/**
* 3openclaw
*
* OpenclawParam
* <pre>
* {
* "input": "{输入信息,必填信息}",
* "prompt": "{模型prompt可选信息}"
* }
* </pre>
*/
@XxlJob("openClawJobHandler")
public void openClawJobHandler() {
// param
String param = XxlJobHelper.getJobParam();
if (param == null || param.trim().isEmpty()) {
XxlJobHelper.log("param is empty.");
XxlJobHelper.handleFail();
return;
}
// openclaw param
OpenClawParam openClawParam = null;
try {
openClawParam = GsonTool.fromJson(param, OpenClawParam.class);
if (openClawParam.getPrompt()==null || openClawParam.getPrompt().isBlank()) {
openClawParam.setPrompt("你是一个出游助手,擅长做旅游规划");
}
if (openClawParam.getInput() == null || openClawParam.getInput().isBlank()) {
XxlJobHelper.log("input is empty.");
XxlJobHelper.handleFail();
return;
}
} catch (Exception e) {
XxlJobHelper.log(new RuntimeException("OpenclawParam parse error", e));
XxlJobHelper.handleFail();
return;
}
// input
XxlJobHelper.log("<br><br><b>【Input】: " + openClawParam.getInput()+ "</b><br><br>");
// build chat-client
ChatClient openclawChatClient = ChatClient
.builder(openAiChatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(MessageWindowChatMemory.builder().build()).build())
.defaultAdvisors(SimpleLoggerAdvisor.builder().build())
.build();
// call opencalw
String response = openclawChatClient
.prompt(openClawParam.getPrompt())
.user(openClawParam.getInput())
.call()
.content();
XxlJobHelper.log("<br><br><b>【Output】: " + response + "</b><br><br>");
}
private static class OpenClawParam {
private String input;
private String prompt;
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public String getPrompt() {
return prompt;
}
public void setPrompt(String prompt) {
this.prompt = prompt;
}
}
}

@ -35,6 +35,9 @@ xxl.job.executor.excludedpackage=
### ollama
spring.ai.model.chat=ollama
### ollama url
spring.ai.ollama.base-url=http://localhost:11434
### openai (for openclaw)
spring.ai.openai.base-url=http://127.0.0.1:18789
spring.ai.openai.api-key=xxxxxx

@ -0,0 +1,95 @@
package com.xxl.job.executor.test.openclaw;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.boot.test.context.SpringBootTest;
import reactor.core.publisher.Flux;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@SpringBootTest
public class OpenClawTest {
private static final Logger logger = LoggerFactory.getLogger(OpenClawTest.class);
@Resource
private OpenAiChatModel openAiChatModel;
/*ChatModel chatModel = OpenAiChatModel
.builder()
.openAiApi(OpenAiApi
.builder()
.baseUrl(baseUrl)
.apiKey( token)
.webClientBuilder(WebClient.builder().clientConnector(
new ReactorClientHttpConnector(
HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30 * 1000)
.responseTimeout(Duration.ofMillis(30 * 1000))
)
))
.build())
.build();*/
@Test
public void test() throws Exception {
String prompt = "你是一个出游助手,擅长做旅游规划";
String input = "查看下上海今天得天气,给出出游建议";
// ChatClient
ChatClient chatClient = ChatClient
.builder(openAiChatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(MessageWindowChatMemory.builder().build()).build())
.defaultAdvisors(SimpleLoggerAdvisor.builder().build())
.build();
// Call LLM: 同步输出
String response = chatClient
.prompt(prompt)
.user(input)
.call()
.content();
logger.info("Input: {}", input);
logger.info("Output: {}", response);
}
@Test
public void test2() throws Exception {
String prompt = "你是一个出游助手,擅长做旅游规划";
String input = "查看下上海今天得天气,给出出游建议";
// ChatClient
ChatClient chatClient = ChatClient
.builder(openAiChatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(MessageWindowChatMemory.builder().build()).build())
.defaultAdvisors(SimpleLoggerAdvisor.builder().build())
.build();
// Call LLM: 流式输出
Flux<String> flux = chatClient
.prompt(prompt)
.user(input)
.user(user -> user.text(input).params(Map.of("stream", true)))
.stream()
.content();
flux.subscribe(
data -> System.out.println("Received: " + data), // onNext 处理
error -> System.err.println("Error: " + error), // onError 处理
() -> System.out.println("Completed") // onComplete 处理
);
TimeUnit.SECONDS.sleep(30);
}
}
Loading…
Cancel
Save