From 7a0969dc4e6322f53bbc84ffbf5cd4c7202bb2ec Mon Sep 17 00:00:00 2001 From: jie65535 Date: Tue, 4 Aug 2026 23:58:47 +0800 Subject: [PATCH] profile: return complete query results --- README.md | 4 +-- .../kotlin/tools/QueryUserProfileAgent.kt | 22 +++++-------- .../kotlin/tools/QueryUserProfileAgentTest.kt | 32 +++++++++++++++++++ 3 files changed, 42 insertions(+), 16 deletions(-) create mode 100644 src/test/kotlin/tools/QueryUserProfileAgentTest.kt diff --git a/README.md b/README.md index 46f3c8f..c4a8a81 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ searchHistoryMaxRecords: 5000 下一次正常群聊会自动携带触发者和最近发言者的认识。现有好感度、Bot 代号、标签和主观印象会与证据驱动的 长期画像按同一个人合并渲染,并明确给出长期画像条目数;私聊也会携带对方的可靠画像摘要和条目数。 -模型需要完整细节时可主动调用 `queryUserProfile`,按 QQ 号、群名片、昵称或好友备注读取画像。两套画像数据 +模型需要完整细节时可主动调用 `queryUserProfile`,按 QQ 号、群名片、昵称或好友备注读取完整画像。两套画像数据 仍独立保存,自动画像不会修改好感度。 `queryUserProfile` 解析出唯一联系人后才按需调用一次 `queryProfile()`,并在内存中缓存 10 分钟;周期联系人 @@ -486,7 +486,7 @@ JChatGPT 维护对每位用户的画像,由好感度、Bot 自定义代号、 ### 注入到上下文 - 群聊:合并列出当前相关群友的关系状态,以及可靠长期画像的摘要和条目数 - 私聊:合并注入对方的关系状态,以及可靠长期画像的摘要和条目数 -- 模型需要画像明细时主动调用 `queryUserProfile`,不必把全部条目常驻在上下文中 +- 模型需要画像明细时主动调用 `queryUserProfile`,查询始终返回完整条目,不必把全部条目常驻在上下文中 - 只有好感度数值、其它关系字段和可靠长期画像均为空的用户不会被列出,避免提示词噪声 ### 配置选项 diff --git a/src/main/kotlin/tools/QueryUserProfileAgent.kt b/src/main/kotlin/tools/QueryUserProfileAgent.kt index 3950521..37f0936 100644 --- a/src/main/kotlin/tools/QueryUserProfileAgent.kt +++ b/src/main/kotlin/tools/QueryUserProfileAgent.kt @@ -5,7 +5,6 @@ import com.aallam.openai.api.core.Parameters import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.booleanOrNull import kotlinx.serialization.json.contentOrNull -import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.longOrNull import kotlinx.serialization.json.put @@ -50,10 +49,6 @@ class QueryUserProfileAgent : BaseAgent( put("type", "boolean") put("description", "是否返回画像条目明细,默认true") } - putJsonObject("limit") { - put("type", "integer") - put("description", "最多返回多少条画像条目,默认20,最大50") - } } } ) @@ -73,10 +68,9 @@ class QueryUserProfileAgent : BaseAgent( if (profile == null && publicProfile == null) return "用户 $userId 尚无画像,当前联系人也没有可读取的公开资料卡。" val includeItems = args?.get("includeItems")?.jsonPrimitive?.booleanOrNull ?: true - val limit = args?.get("limit")?.jsonPrimitive?.intOrNull?.coerceIn(1, 50) ?: 20 val displayName = resolveDisplayName(userId, event) - return formatProfile(userId, profile, publicProfile, displayName, includeItems, limit) + return formatProfile(userId, profile, publicProfile, displayName, includeItems) } private fun resolveUserId(args: JsonObject?, event: MessageEvent): Long? { @@ -148,7 +142,6 @@ class QueryUserProfileAgent : BaseAgent( publicProfile: UserProfile?, displayName: String, includeItems: Boolean, - limit: Int, ): String = buildString { appendLine("用户画像:$displayName($userId)") if (profile != null) { @@ -163,9 +156,7 @@ class QueryUserProfileAgent : BaseAgent( publicProfile?.let { appendPublicProfile(it) } if (includeItems && profile?.items?.isNotEmpty() == true) { appendLine("条目:") - profile.items - .sortedWith(compareBy({ it.category.ordinal }, { it.firstSeenAt }, { it.id })) - .take(limit) + selectProfileItems(profile.items) .forEach { item -> append("- ") append(item.category.label()).append('/').append(item.confidence.name.lowercase()) @@ -180,9 +171,6 @@ class QueryUserProfileAgent : BaseAgent( relationship = item.category == ProfileCategory.RELATIONSHIP_NOTE, )) } - if (profile.items.size > limit) { - appendLine("其余 ${profile.items.size - limit} 条已省略,可提高 limit 继续查询。") - } } }.trim() @@ -252,3 +240,9 @@ class QueryUserProfileAgent : BaseAgent( private fun String.normalized(): String = trim().replace(Regex("\\s+"), " ") } + +internal fun selectProfileItems( + items: List, +): List = items.sortedWith( + compareBy({ it.category.ordinal }, { it.firstSeenAt }, { it.id }) + ) diff --git a/src/test/kotlin/tools/QueryUserProfileAgentTest.kt b/src/test/kotlin/tools/QueryUserProfileAgentTest.kt new file mode 100644 index 0000000..2bb2456 --- /dev/null +++ b/src/test/kotlin/tools/QueryUserProfileAgentTest.kt @@ -0,0 +1,32 @@ +package top.jie65535.mirai.tools + +import top.jie65535.mirai.profile.ProfileCategory +import top.jie65535.mirai.profile.ProfileConfidence +import top.jie65535.mirai.profile.UserProfileItem +import kotlin.test.Test +import kotlin.test.assertEquals + +class QueryUserProfileAgentTest { + @Test + fun returnsAllItemsInStableOrder() { + val items = listOf( + item("later", firstSeenAt = 300), + item("earlier", firstSeenAt = 100), + item("middle", firstSeenAt = 200), + ) + + assertEquals( + listOf("earlier", "middle", "later"), + selectProfileItems(items).map(UserProfileItem::content), + ) + } + + private fun item(content: String, firstSeenAt: Int) = UserProfileItem( + id = content, + category = ProfileCategory.NOTABLE_FACT, + content = content, + confidence = ProfileConfidence.MEDIUM, + firstSeenAt = firstSeenAt, + lastConfirmedAt = firstSeenAt, + ) +}