mirror of
https://github.com/jie65535/JChatGPT.git
synced 2026-06-23 00:49:31 +08:00
Extend favorability into user profile system
This commit is contained in:
@@ -318,17 +318,27 @@ object JChatGPT : KotlinPlugin(
|
|||||||
var lastId = 0L
|
var lastId = 0L
|
||||||
if (event is GroupMessageEvent) {
|
if (event is GroupMessageEvent) {
|
||||||
if (PluginConfig.enableFavorabilitySystem) {
|
if (PluginConfig.enableFavorabilitySystem) {
|
||||||
val favorabilityInfos = history.map { it.fromId }
|
val knownUsers = history.asSequence()
|
||||||
|
.map { it.fromId }
|
||||||
.filter { it != event.bot.id }
|
.filter { it != event.bot.id }
|
||||||
.distinct()
|
.distinct()
|
||||||
.mapNotNull { PluginData.userFavorability[it] }
|
.mapNotNull { PluginData.userFavorability[it] }
|
||||||
if (favorabilityInfos.isNotEmpty()) {
|
.filter { it.name.isNotEmpty() || it.tags.isNotEmpty() || it.impression.isNotEmpty() }
|
||||||
historyText.appendLine("## 相关成员的好感信息")
|
.sortedBy { it.userId }
|
||||||
for (info in favorabilityInfos) {
|
.toList()
|
||||||
historyText.append(getNameCard(event.group, info.userId)).append('\t')
|
if (knownUsers.isNotEmpty()) {
|
||||||
.appendLine(info).appendLine()
|
historyText.appendLine("【你认识的群友】")
|
||||||
|
for (info in knownUsers) {
|
||||||
|
val displayName = if (info.name.isNotEmpty()) info.name
|
||||||
|
else getNameCard(event.subject, info.userId)
|
||||||
|
historyText.append("- ").append(displayName)
|
||||||
|
.append("(${info.userId})")
|
||||||
|
.append(" 好感度${if (info.value >= 0) "+" else ""}${info.value}")
|
||||||
|
if (info.tags.isNotEmpty()) historyText.append(" [${info.tags.joinToString(", ")}]")
|
||||||
|
if (info.impression.isNotEmpty()) historyText.append(" ${info.impression}")
|
||||||
|
historyText.appendLine()
|
||||||
}
|
}
|
||||||
historyText.appendLine("---").appendLine()
|
historyText.appendLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,10 +351,15 @@ object JChatGPT : KotlinPlugin(
|
|||||||
} else {
|
} else {
|
||||||
if (PluginConfig.enableFavorabilitySystem) {
|
if (PluginConfig.enableFavorabilitySystem) {
|
||||||
val favorabilityInfo = PluginData.userFavorability[event.sender.id]
|
val favorabilityInfo = PluginData.userFavorability[event.sender.id]
|
||||||
if (favorabilityInfo != null) {
|
if (favorabilityInfo != null && (favorabilityInfo.name.isNotEmpty() || favorabilityInfo.tags.isNotEmpty() || favorabilityInfo.impression.isNotEmpty())) {
|
||||||
historyText.append("你对\"").append(event.senderName).append("\"的好感信息如下: ")
|
val displayName = if (favorabilityInfo.name.isNotEmpty()) favorabilityInfo.name else event.senderName
|
||||||
.appendLine(favorabilityInfo).appendLine()
|
historyText.appendLine("【你认识的对方】")
|
||||||
historyText.appendLine("---").appendLine()
|
historyText.append("- ").append(displayName)
|
||||||
|
.append("(${event.sender.id})")
|
||||||
|
.append(" 好感度${if (favorabilityInfo.value >= 0) "+" else ""}${favorabilityInfo.value}")
|
||||||
|
if (favorabilityInfo.tags.isNotEmpty()) historyText.append(" [${favorabilityInfo.tags.joinToString(", ")}]")
|
||||||
|
if (favorabilityInfo.impression.isNotEmpty()) historyText.append(" ${favorabilityInfo.impression}")
|
||||||
|
historyText.appendLine().appendLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,22 +10,27 @@ import net.mamoe.mirai.console.data.value
|
|||||||
* @param value 好感度值 (-100 ~ 100)
|
* @param value 好感度值 (-100 ~ 100)
|
||||||
* @param reasons 调整原因列表,用于溯源
|
* @param reasons 调整原因列表,用于溯源
|
||||||
* @param impression 对用户的印象/画像
|
* @param impression 对用户的印象/画像
|
||||||
|
* @param name Bot给此人起的代号
|
||||||
|
* @param tags 标签列表,最多5个
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FavorabilityInfo(
|
data class FavorabilityInfo(
|
||||||
val userId: Long,
|
val userId: Long,
|
||||||
val value: Int = 0,
|
val value: Int = 0,
|
||||||
val reasons: List<String> = emptyList(),
|
val reasons: List<String> = emptyList(),
|
||||||
val impression: String = ""
|
val impression: String = "",
|
||||||
|
val name: String = "",
|
||||||
|
val tags: List<String> = emptyList()
|
||||||
) {
|
) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return buildString {
|
return buildString {
|
||||||
append("好感度:$value")
|
append("好感度:$value")
|
||||||
if (impression.isNotEmpty()) {
|
if (name.isNotEmpty()) append(",代号:$name")
|
||||||
append("\t印象:$impression")
|
if (tags.isNotEmpty()) append(",标签:[${tags.joinToString(", ")}]")
|
||||||
}
|
if (impression.isNotEmpty()) append(",印象:$impression")
|
||||||
if (reasons.isNotEmpty()) {
|
if (reasons.isNotEmpty()) {
|
||||||
appendLine("\t调整原因:")
|
appendLine()
|
||||||
|
appendLine("调整原因:")
|
||||||
reasons.forEach { reason ->
|
reasons.forEach { reason ->
|
||||||
appendLine("* $reason")
|
appendLine("* $reason")
|
||||||
}
|
}
|
||||||
@@ -99,4 +104,4 @@ object PluginData : AutoSavePluginData("data") {
|
|||||||
.replace("\n\n", "\n")
|
.replace("\n\n", "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package top.jie65535.mirai.tools
|
|||||||
|
|
||||||
import com.aallam.openai.api.chat.Tool
|
import com.aallam.openai.api.chat.Tool
|
||||||
import com.aallam.openai.api.core.Parameters
|
import com.aallam.openai.api.core.Parameters
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.add
|
import kotlinx.serialization.json.add
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
@@ -11,6 +12,7 @@ import kotlinx.serialization.json.jsonPrimitive
|
|||||||
import kotlinx.serialization.json.longOrNull
|
import kotlinx.serialization.json.longOrNull
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
import kotlinx.serialization.json.putJsonArray
|
import kotlinx.serialization.json.putJsonArray
|
||||||
|
import kotlinx.serialization.json.putJsonObject
|
||||||
import net.mamoe.mirai.event.events.MessageEvent
|
import net.mamoe.mirai.event.events.MessageEvent
|
||||||
import top.jie65535.mirai.JChatGPT
|
import top.jie65535.mirai.JChatGPT
|
||||||
import top.jie65535.mirai.PluginData
|
import top.jie65535.mirai.PluginData
|
||||||
@@ -21,9 +23,20 @@ import java.time.format.DateTimeFormatter
|
|||||||
class AdjustUserFavorabilityAgent : BaseAgent(
|
class AdjustUserFavorabilityAgent : BaseAgent(
|
||||||
tool = Tool.function(
|
tool = Tool.function(
|
||||||
name = "adjustUserFavorability",
|
name = "adjustUserFavorability",
|
||||||
description = "可根据网友行为调整对其好感度,范围从-100到100。" +
|
description = """
|
||||||
"默认为0表示陌生人,100表示非常好的朋友,-100表示已拉黑。" +
|
维护你对群友的认识(好感度、印象、标签、代号)。
|
||||||
"当好感度低于0时,有一定概率忽略该用户的消息,-100则100%忽略其消息。",
|
每次和某人有实质交流后建议调用一次,可与发言工具在同一轮发出,几乎不产生额外成本。
|
||||||
|
|
||||||
|
触发场景:
|
||||||
|
- 首次有像样的对话(建立初始印象和代号)
|
||||||
|
- 对方透露身份/职业/偏好/技术栈(加 tag)
|
||||||
|
- 互动产生明显情绪变化——开心/被逗/被冒犯(调 change)
|
||||||
|
- 已有印象明显不准(更新 impression)
|
||||||
|
|
||||||
|
change 默认为 0,只更新标签/印象时不用填 reason。
|
||||||
|
tags 上限 5 个,满了须先 tags_remove 旧标签才能继续添加。
|
||||||
|
好感度范围 -100 到 100,低于 0 时有概率忽略其消息,-100 则 100% 忽略。
|
||||||
|
""".trimIndent(),
|
||||||
parameters = Parameters.buildJsonObject {
|
parameters = Parameters.buildJsonObject {
|
||||||
put("type", "object")
|
put("type", "object")
|
||||||
put("properties", buildJsonObject {
|
put("properties", buildJsonObject {
|
||||||
@@ -33,21 +46,33 @@ class AdjustUserFavorabilityAgent : BaseAgent(
|
|||||||
})
|
})
|
||||||
put("change", buildJsonObject {
|
put("change", buildJsonObject {
|
||||||
put("type", "integer")
|
put("type", "integer")
|
||||||
put("description", "好感度变化值(可正可负)")
|
put("description", "好感度变化值(可正可负),默认为0")
|
||||||
})
|
})
|
||||||
put("reason", buildJsonObject {
|
put("reason", buildJsonObject {
|
||||||
put("type", "string")
|
put("type", "string")
|
||||||
put("description", "调整原因(供日志记录和溯源)")
|
put("description", "调整原因(change!=0时建议填写,供溯源)")
|
||||||
})
|
})
|
||||||
put("impression", buildJsonObject {
|
put("impression", buildJsonObject {
|
||||||
put("type", "string")
|
put("type", "string")
|
||||||
put("description", "对用户的印象或称呼(可选)")
|
put("description", "对用户的印象描述,覆盖旧值(上限200字符)")
|
||||||
|
})
|
||||||
|
put("name", buildJsonObject {
|
||||||
|
put("type", "string")
|
||||||
|
put("description", "Bot给此人起的代号(非QQ昵称,上限20字符)")
|
||||||
|
})
|
||||||
|
put("tags_add", buildJsonObject {
|
||||||
|
put("type", "array")
|
||||||
|
putJsonObject("items") { put("type", "string") }
|
||||||
|
put("description", "追加标签(自动去重,总数超5项返错,单项上限20字符)")
|
||||||
|
})
|
||||||
|
put("tags_remove", buildJsonObject {
|
||||||
|
put("type", "array")
|
||||||
|
putJsonObject("items") { put("type", "string") }
|
||||||
|
put("description", "删除标签(不存在的项静默忽略)")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
putJsonArray("required") {
|
putJsonArray("required") {
|
||||||
add("userId")
|
add("userId")
|
||||||
add("change")
|
|
||||||
add("reason")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -56,48 +81,66 @@ class AdjustUserFavorabilityAgent : BaseAgent(
|
|||||||
requireNotNull(args)
|
requireNotNull(args)
|
||||||
|
|
||||||
val userId = args["userId"]?.jsonPrimitive?.longOrNull
|
val userId = args["userId"]?.jsonPrimitive?.longOrNull
|
||||||
val change = args["change"]?.jsonPrimitive?.intOrNull
|
?: return "错误:userId参数不能为空"
|
||||||
|
|
||||||
|
val change = args["change"]?.jsonPrimitive?.intOrNull ?: 0
|
||||||
val reason = args["reason"]?.jsonPrimitive?.contentOrNull
|
val reason = args["reason"]?.jsonPrimitive?.contentOrNull
|
||||||
val impression = args["impression"]?.jsonPrimitive?.contentOrNull
|
val impression = args["impression"]?.jsonPrimitive?.contentOrNull
|
||||||
|
val name = args["name"]?.jsonPrimitive?.contentOrNull
|
||||||
|
val tagsAdd = (args["tags_add"] as? JsonArray)?.mapNotNull { it.jsonPrimitive.contentOrNull }
|
||||||
|
val tagsRemove = (args["tags_remove"] as? JsonArray)?.mapNotNull { it.jsonPrimitive.contentOrNull }
|
||||||
|
|
||||||
if (userId == null || change == null || reason == null) {
|
// 字段长度校验
|
||||||
return "错误:userId、change和reason参数不能为空"
|
if (name != null && name.length > 20) return "错误:name不能超过20字符(当前${name.length}字符)"
|
||||||
|
if (impression != null && impression.length > 200) return "错误:impression不能超过200字符(当前${impression.length}字符)"
|
||||||
|
tagsAdd?.forEach { tag ->
|
||||||
|
if (tag.length > 20) return "错误:tag「$tag」不能超过20字符"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前好感度信息
|
|
||||||
val currentInfo = PluginData.userFavorability[userId] ?: FavorabilityInfo(userId)
|
val currentInfo = PluginData.userFavorability[userId] ?: FavorabilityInfo(userId)
|
||||||
val currentValue = currentInfo.value
|
val currentValue = currentInfo.value
|
||||||
|
|
||||||
// 计算新的好感度值,限制在-100~100范围内
|
|
||||||
val newValue = (currentValue + change).coerceIn(-100, 100)
|
val newValue = (currentValue + change).coerceIn(-100, 100)
|
||||||
|
|
||||||
// 更新原因列表
|
// 只在 change != 0 时记录原因
|
||||||
val timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
|
val newReasons = if (change != 0 && reason != null) {
|
||||||
val newReason = "${timeFormatter.format(OffsetDateTime.now())}: $reason"
|
val timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
|
||||||
val newReasons = if (currentInfo.reasons.size >= 10) {
|
val newReason = "${timeFormatter.format(OffsetDateTime.now())}: $reason"
|
||||||
// 保留最近的10条原因记录
|
if (currentInfo.reasons.size >= 10) {
|
||||||
(currentInfo.reasons.drop(1) + newReason)
|
currentInfo.reasons.drop(1) + newReason
|
||||||
} else {
|
} else {
|
||||||
(currentInfo.reasons + newReason)
|
currentInfo.reasons + newReason
|
||||||
|
}
|
||||||
|
} else currentInfo.reasons
|
||||||
|
|
||||||
|
// 处理标签
|
||||||
|
val newTags = currentInfo.tags.toMutableList()
|
||||||
|
tagsRemove?.forEach { tag -> newTags.remove(tag) }
|
||||||
|
if (tagsAdd != null) {
|
||||||
|
val toAdd = tagsAdd.filter { it !in newTags }
|
||||||
|
if (newTags.size + toAdd.size > 5) {
|
||||||
|
return "错误:标签已满(当前${newTags.size}项),须先用tags_remove删除旧标签。当前标签:[${newTags.joinToString(", ")}]"
|
||||||
|
}
|
||||||
|
newTags.addAll(toAdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新印象/画像
|
|
||||||
val newImpression = impression ?: currentInfo.impression
|
|
||||||
|
|
||||||
// 创建新的好感度信息
|
|
||||||
val newInfo = FavorabilityInfo(
|
val newInfo = FavorabilityInfo(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
value = newValue,
|
value = newValue,
|
||||||
reasons = newReasons,
|
reasons = newReasons,
|
||||||
impression = newImpression
|
impression = impression ?: currentInfo.impression,
|
||||||
|
name = name ?: currentInfo.name,
|
||||||
|
tags = newTags
|
||||||
)
|
)
|
||||||
|
|
||||||
// 更新好感度
|
|
||||||
PluginData.userFavorability[userId] = newInfo
|
PluginData.userFavorability[userId] = newInfo
|
||||||
|
JChatGPT.logger.info("用户 $userId 画像已更新:好感度($currentValue -> $newValue),原因:$reason")
|
||||||
|
|
||||||
// 记录日志
|
return buildString {
|
||||||
JChatGPT.logger.info("用户 $userId 的好感度 ($currentValue -> $newValue),原因:$reason")
|
append("用户 $userId 画像已更新:好感度=$newValue")
|
||||||
|
if (newTags.isNotEmpty()) append(",标签=[${newTags.joinToString(", ")}]")
|
||||||
return "用户 $userId 的好感度已更新为 $newValue"
|
if (newInfo.name.isNotEmpty()) append(",代号=${newInfo.name}")
|
||||||
|
if (newInfo.impression.isNotEmpty()) append(",印象=${newInfo.impression}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user