mirror of
https://github.com/jie65535/JChatGPT.git
synced 2026-09-15 02:56:10 +08:00
profile: handle rejected model responses
This commit is contained in:
@@ -8,6 +8,8 @@ import top.jie65535.mirai.JChatGPT
|
|||||||
import top.jie65535.mirai.config.PluginConfig
|
import top.jie65535.mirai.config.PluginConfig
|
||||||
import top.jie65535.mirai.data.ChatHistoryStore
|
import top.jie65535.mirai.data.ChatHistoryStore
|
||||||
import top.jie65535.mirai.llm.LargeLanguageModels
|
import top.jie65535.mirai.llm.LargeLanguageModels
|
||||||
|
import top.jie65535.mirai.llm.ModelRequestRejectedException
|
||||||
|
import top.jie65535.mirai.llm.ModelSafetyRejectionException
|
||||||
import top.jie65535.mirai.util.RetryBackoff
|
import top.jie65535.mirai.util.RetryBackoff
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
@@ -334,15 +336,23 @@ object UserProfileAnalysisService {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
val report = analyzeConversationBatch(
|
val report = try {
|
||||||
batch = batch,
|
analyzeConversationBatch(
|
||||||
minAuthoredTextChars = PluginConfig.profileAutoMinAuthoredTextChars,
|
batch = batch,
|
||||||
model = model,
|
minAuthoredTextChars = PluginConfig.profileAutoMinAuthoredTextChars,
|
||||||
retryMax = PluginConfig.profileRetryMax,
|
model = model,
|
||||||
summaryMaxLength = PluginConfig.profileSummaryMaxLength,
|
retryMax = PluginConfig.profileRetryMax,
|
||||||
onRetryFailure = { message, cause -> JChatGPT.logger.warning(message, cause) },
|
summaryMaxLength = PluginConfig.profileSummaryMaxLength,
|
||||||
onCommittedOperations = ProfileOperationLogger::log,
|
onRetryFailure = { message, cause -> JChatGPT.logger.warning(message, cause) },
|
||||||
)
|
onCommittedOperations = ProfileOperationLogger::log,
|
||||||
|
)
|
||||||
|
} catch (cause: ModelSafetyRejectionException) {
|
||||||
|
JChatGPT.logger.warning(
|
||||||
|
"群 $groupId 会话画像 [${batch.startTime}, ${batch.endTime}) 被模型安全策略拒绝," +
|
||||||
|
"已跳过该批并继续后续历史 code=${cause.errorCode ?: "unknown"}"
|
||||||
|
)
|
||||||
|
null
|
||||||
|
}
|
||||||
cursor = cursor.copy(
|
cursor = cursor.copy(
|
||||||
cursorTime = batch.endTime,
|
cursorTime = batch.endTime,
|
||||||
updatedAt = System.currentTimeMillis(),
|
updatedAt = System.currentTimeMillis(),
|
||||||
@@ -545,6 +555,14 @@ object UserProfileAnalysisService {
|
|||||||
return result to reductions
|
return result to reductions
|
||||||
} catch (cause: Exception) {
|
} catch (cause: Exception) {
|
||||||
if (cause is CancellationException) throw cause
|
if (cause is CancellationException) throw cause
|
||||||
|
if (cause is ModelRequestRejectedException) {
|
||||||
|
onRetryFailure(
|
||||||
|
"群 ${batch.groupId} 会话画像 [${batch.startTime}, ${batch.endTime}) " +
|
||||||
|
"被模型拒绝,已停止重试",
|
||||||
|
cause,
|
||||||
|
)
|
||||||
|
throw cause
|
||||||
|
}
|
||||||
lastFailure = cause
|
lastFailure = cause
|
||||||
handleRetryFailure(
|
handleRetryFailure(
|
||||||
attempt = attempt,
|
attempt = attempt,
|
||||||
@@ -591,6 +609,14 @@ object UserProfileAnalysisService {
|
|||||||
return result to reduction
|
return result to reduction
|
||||||
} catch (cause: Exception) {
|
} catch (cause: Exception) {
|
||||||
if (cause is CancellationException) throw cause
|
if (cause is CancellationException) throw cause
|
||||||
|
if (cause is ModelRequestRejectedException) {
|
||||||
|
JChatGPT.logger.warning(
|
||||||
|
"用户 ${batch.userId} 画像批次 [${batch.startTime}, ${batch.endTime}) " +
|
||||||
|
"被模型拒绝,已停止重试",
|
||||||
|
cause,
|
||||||
|
)
|
||||||
|
throw cause
|
||||||
|
}
|
||||||
lastFailure = cause
|
lastFailure = cause
|
||||||
handleRetryFailure(
|
handleRetryFailure(
|
||||||
attempt = attempt,
|
attempt = attempt,
|
||||||
@@ -631,6 +657,13 @@ object UserProfileAnalysisService {
|
|||||||
return result to plan
|
return result to plan
|
||||||
} catch (cause: Exception) {
|
} catch (cause: Exception) {
|
||||||
if (cause is CancellationException) throw cause
|
if (cause is CancellationException) throw cause
|
||||||
|
if (cause is ModelRequestRejectedException) {
|
||||||
|
JChatGPT.logger.warning(
|
||||||
|
"用户 ${profile.userId} 画像压缩被模型拒绝,已停止重试",
|
||||||
|
cause,
|
||||||
|
)
|
||||||
|
throw cause
|
||||||
|
}
|
||||||
lastFailure = cause
|
lastFailure = cause
|
||||||
handleRetryFailure(
|
handleRetryFailure(
|
||||||
attempt = attempt,
|
attempt = attempt,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||||
import top.jie65535.mirai.data.ChatMessageRecord
|
import top.jie65535.mirai.data.ChatMessageRecord
|
||||||
|
import top.jie65535.mirai.llm.ModelSafetyRejectionException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
@@ -122,6 +123,25 @@ class UserProfileAnalysisServiceTest {
|
|||||||
assertFalse(UserProfileStore.isConversationProcessed(INPUT_HASH))
|
assertFalse(UserProfileStore.isConversationProcessed(INPUT_HASH))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun safetyRejectionDoesNotRetryOrCommit() = withProfileStore {
|
||||||
|
val model = FakeConversationProfileModel {
|
||||||
|
throw ModelSafetyRejectionException(
|
||||||
|
errorType = "invalid_request_error",
|
||||||
|
errorCode = "cyber_policy",
|
||||||
|
message = "blocked by policy",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFailsWith<ModelSafetyRejectionException> {
|
||||||
|
analyze(batch(), model, retryMax = 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, model.calls)
|
||||||
|
assertNull(UserProfileStore.load(USER_A))
|
||||||
|
assertFalse(UserProfileStore.isConversationProcessed(INPUT_HASH))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun skipsConversationAlreadyProcessedByAnEarlierCall() = withProfileStore {
|
fun skipsConversationAlreadyProcessedByAnEarlierCall() = withProfileStore {
|
||||||
val model = FakeConversationProfileModel { successfulResult() }
|
val model = FakeConversationProfileModel { successfulResult() }
|
||||||
|
|||||||
Reference in New Issue
Block a user