Replace ImageEdit with ImageAgent for Qwen Image 2.0

Supports text-to-image, single-image edit and multi-image fusion (0-3
reference images) via a single tool. Renames imageEditModel config to
imageModel (default qwen-image-2.0) and adds imageWatermark toggle.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 15:47:08 +08:00
parent 98bb1066c4
commit 39b49bb302
4 changed files with 44 additions and 34 deletions

View File

@@ -91,8 +91,10 @@ reasoningModelExtraBody: ''
visualModelExtraBody: '' visualModelExtraBody: ''
# 百炼平台API KEY # 百炼平台API KEY
dashScopeApiKey: '' dashScopeApiKey: ''
# 百炼平台图片编辑模型 # 百炼平台图像模型(文生图 + 图像编辑)
imageEditModel: 'qwen-image-edit' imageModel: 'qwen-image-2.0'
# 是否在生成图片右下角添加 Qwen-Image 水印
imageWatermark: false
# 百炼平台TTS模型 # 百炼平台TTS模型
ttsModel: 'qwen-tts' ttsModel: 'qwen-tts'
# Jina API Key # Jina API Key
@@ -280,7 +282,7 @@ JChatGPT 默认配置为使用阿里云百炼平台的通义千问系列模型
6. **GroupManageAgent** - 群管理功能(如禁言) 6. **GroupManageAgent** - 群管理功能(如禁言)
7. **SendSingleMessage/CompositeMessage** - 发送消息 7. **SendSingleMessage/CompositeMessage** - 发送消息
8. **SendVoiceMessage** - 发送语音消息 8. **SendVoiceMessage** - 发送语音消息
9. **ImageEdit** - 图像编辑 9. **ImageAgent** - 图像生成与编辑(文生图、单图编辑、多图融合)
10. **WeatherService** - 天气查询 10. **WeatherService** - 天气查询
11. **SearchChatHistory** - 按关键词、发送者、时间范围搜索群聊消息历史(依赖 mirai-hibernate-plugin 11. **SearchChatHistory** - 按关键词、发送者、时间范围搜索群聊消息历史(依赖 mirai-hibernate-plugin

View File

@@ -848,8 +848,8 @@ object JChatGPT : KotlinPlugin(
// 视觉代理 // 视觉代理
VisualAgent(), VisualAgent(),
// 图像编辑模型 // 图像生成与编辑
ImageEdit(), ImageAgent(),
// 天气服务 // 天气服务
WeatherService(), WeatherService(),

View File

@@ -50,8 +50,11 @@ object PluginConfig : AutoSavePluginConfig("Config") {
@ValueDescription("百炼平台API KEY") @ValueDescription("百炼平台API KEY")
val dashScopeApiKey: String by value("") val dashScopeApiKey: String by value("")
@ValueDescription("百炼平台图片编辑模型") @ValueDescription("百炼平台图像模型支持文生图与图像编辑。可选qwen-image-2.0 / qwen-image-2.0-pro / qwen-image-edit-max / qwen-image-edit-plus 等")
val imageEditModel: String by value("qwen-image-edit") val imageModel: String by value("qwen-image-2.0")
@ValueDescription("是否在生成的图片右下角添加 Qwen-Image 水印")
val imageWatermark: Boolean by value(false)
@ValueDescription("百炼平台TTS模型") @ValueDescription("百炼平台TTS模型")
val ttsModel: String by value("qwen-tts") val ttsModel: String by value("qwen-tts")

View File

@@ -22,29 +22,30 @@ import kotlinx.serialization.json.putJsonObject
import top.jie65535.mirai.JChatGPT import top.jie65535.mirai.JChatGPT
import top.jie65535.mirai.PluginConfig import top.jie65535.mirai.PluginConfig
class ImageEdit : BaseAgent( class ImageAgent : BaseAgent(
tool = Tool.function( tool = Tool.function(
name = "imageEdit", name = "imageAgent",
description = "可通过调用图像编辑模型来修改图片。备注:该方法成本较高,非必要尽量不要调用。编辑图片前无需识别图片内容,图像编辑模型自己会理解图片内容!", description = "调用千问图像模型生成或编辑图片。不传 image_urls 即纯文生图;" +
"传 1~3 张图片可进行编辑、修改或多图融合。" +
"备注:该方法成本较高,非必要尽量不要调用。" +
"编辑图片前无需识别图片内容,模型自己会理解图片内容。",
parameters = Parameters.buildJsonObject { parameters = Parameters.buildJsonObject {
put("type", "object") put("type", "object")
putJsonObject("properties") { putJsonObject("properties") {
putJsonObject("image_url") { putJsonObject("image_urls") {
put("type", "string") put("type", "array")
put("description", "原始图片地址") putJsonObject("items") {
put("type", "string")
}
put("description", "参考图片地址列表,可传 0~3 张。" +
"不传或为空即纯文生图;传 1 张为编辑;多张为融合,输出比例与最后一张对齐。")
} }
putJsonObject("prompt") { putJsonObject("prompt") {
put("type", "string") put("type", "string")
put("description", "正向提示词,用来描述需要对图片进行修改的要求") put("description", "提示词,描述期望生成或修改的画面内容")
} }
// putJsonObject("negative_prompt") {
// put("type", "string")
// put("description", "反向提示词,用来描述不希望在画面中看到的内容,可以对画面进行限制。" +
// "示例值:低分辨率、错误、最差质量、低质量、残缺、多余的手指、比例不良等。")
// }
} }
putJsonArray("required") { putJsonArray("required") {
add("image_url")
add("prompt") add("prompt")
} }
} }
@@ -58,25 +59,29 @@ class ImageEdit : BaseAgent(
get() = PluginConfig.dashScopeApiKey.isNotEmpty() get() = PluginConfig.dashScopeApiKey.isNotEmpty()
override val loadingMessage: String override val loadingMessage: String
get() = "图中..." get() = "图中..."
override suspend fun execute(args: JsonObject?): String { override suspend fun execute(args: JsonObject?): String {
requireNotNull(args) requireNotNull(args)
val imageUrl = args.getValue("image_url").jsonPrimitive.content
val prompt = args.getValue("prompt").jsonPrimitive.content val prompt = args.getValue("prompt").jsonPrimitive.content
// val negativePrompt = args["negative_prompt"]?.jsonPrimitive?.content val imageUrls = args["image_urls"]?.jsonArray
?.map { it.jsonPrimitive.content }
?: emptyList()
val response = httpClient.post(API_URL) { val response = httpClient.post(API_URL) {
contentType(ContentType("application", "json")) contentType(ContentType("application", "json"))
header("Authorization", "Bearer " + PluginConfig.dashScopeApiKey) header("Authorization", "Bearer " + PluginConfig.dashScopeApiKey)
setBody(buildJsonObject { setBody(buildJsonObject {
put("model", PluginConfig.imageEditModel) put("model", PluginConfig.imageModel)
putJsonObject("input") { putJsonObject("input") {
putJsonArray("messages") { putJsonArray("messages") {
addJsonObject { addJsonObject {
put("role", "user") put("role", "user")
putJsonArray("content") { putJsonArray("content") {
addJsonObject { for (url in imageUrls) {
put("image", imageUrl) addJsonObject {
put("image", url)
}
} }
addJsonObject { addJsonObject {
put("text", prompt) put("text", prompt)
@@ -85,11 +90,11 @@ class ImageEdit : BaseAgent(
} }
} }
} }
// if (negativePrompt != null) { putJsonObject("parameters") {
// putJsonObject("parameters") { put("n", 1)
// put("negative_prompt", negativePrompt) put("prompt_extend", true)
// } put("watermark", PluginConfig.imageWatermark)
// } }
}.toString()) }.toString())
} }
@@ -102,9 +107,9 @@ class ImageEdit : BaseAgent(
.getValue("message").jsonObject .getValue("message").jsonObject
.getValue("content").jsonArray[0].jsonObject .getValue("content").jsonArray[0].jsonObject
.getValue("image").jsonPrimitive.content .getValue("image").jsonPrimitive.content
"图片已编辑完发送时请务必包含完整的url和查询参数因为下载地址存在鉴权![图片]($url)" "图片已发送时请务必包含完整的url和查询参数因为下载地址存在鉴权![图片]($url)"
} catch (e: Throwable) { } catch (e: Throwable) {
JChatGPT.logger.error("图像编辑结果解析异常", e) JChatGPT.logger.error("图像生成结果解析异常", e)
responseJson responseJson
} }
} }