提交 b39a21bd authored 作者: 王鹏飞's avatar 王鹏飞

feat: 更新评分逻辑,支持AI评分选项和评分详情展示

上级 5e72e11b
import { getScoreList, getScoreDetail, updateScore } from '../api'
import { useUserStore } from '@/stores/user'
import { useChat } from '@ezijing/ai-vue'
// import { generatePrompt } from '../prompt'
// import { generatePrompt as generatePrompt2 } from '../prompt2'
import { ElMessage } from 'element-plus'
import {
importType,
......@@ -28,9 +26,10 @@ interface ScoreItem {
comment: string
}
export function useScore() {
export function useScore(options: { includeAIScore?: boolean } = {}) {
const userStore = useUserStore()
const isLoading = ref(false)
const includeAIScore = options.includeAIScore ?? true
const { generateText } = useChat({ provider: 'volcano' })
const fetchScoreList = async () => {
......@@ -92,74 +91,75 @@ export function useScore() {
const liveData = detail.live_data
const competitionRule = detail.competition_rule
let prompt = ''
// if (competitionRule?.competition == 1) {
// if (competitionRule?.questions == 1) {
// prompt = generatePrompt(detail.live_data)
// } else {
// prompt = generatePrompt2(detail.live_data)
// }
// } else {
const rules = JSON.parse(competitionRule?.config || '{}')?.rules || []
rules.forEach((item: any) => {
if (item.rule_mode != 2) return
if (item.type == 1) {
prompt += `请根据选手提交的商品品类信息进行评分(满分${item.percent}分):
选手提交内容:
${JSON.stringify(liveData.commodity_types)}`
} else if (item.type == 2) {
prompt += `请根据选手提交的商品属性配置信息进行评分(满分${item.percent}分):
重要性字典:${JSON.stringify(importType)}
必要性字典:${JSON.stringify(requiredType)}
选手提交内容:
${JSON.stringify(liveData.commodity_attrs)}`
} else if (item.type == 3) {
prompt += `请根据选手提交的商品信息维护内容进行评分(满分${item.percent}分):
发货模式字典:${JSON.stringify(deliveryMode)}
const ruleDataMap: Record<number, { key: string; label: string; data: unknown; extra?: string }> = {
1: {
key: 'commodity_type',
label: '商品品类信息',
data: liveData.commodity_types,
},
2: {
key: 'commodity_attr',
label: '商品属性配置信息',
data: liveData.commodity_attrs,
extra: `重要性字典:${JSON.stringify(importType)}
必要性字典:${JSON.stringify(requiredType)}`,
},
3: {
key: 'commodity',
label: '商品信息维护内容',
data: liveData.commodities,
extra: `发货模式字典:${JSON.stringify(deliveryMode)}
发货时效字典:${JSON.stringify(deliveryTime)}
订单库存扣减规则字典:${JSON.stringify(orderStockCount)}
运费模板字典:${JSON.stringify(shippingTemplate)}
售后政策字典:${JSON.stringify(afterSalesPolicy)}
选手提交内容:
${JSON.stringify(liveData.commodities)}`
} else if (item.type == 4) {
prompt += `请根据选手提交的直播话术进行评分(满分${item.percent}分):
选手提交内容:
${JSON.stringify(liveData.speeches)}`
}
prompt += `评分标准:
${item.content}`
})
prompt += `EXAMPLE JSON OUTPUT:
{
"commodity_type": {
"score": 5,
"comment": "这是评语"
售后政策字典:${JSON.stringify(afterSalesPolicy)}`,
},
"commodity_attr": {
"score": 5,
"comment": "这是评语"
4: {
key: 'speech',
label: '直播话术',
data: liveData.speeches,
},
"commodity": {
"score": 5,
"comment": "这是评语"
5: {
key: 'practice_record1',
label: '直播演练',
data: liveData.practices?.[0]?.records?.[0]?.subtitle,
},
"speech": {
"score": 5,
"comment": "这是评语"
6: {
key: 'report',
label: '直播总结汇报',
data: liveData.reports,
},
}`
// }
}
const autoRules = rules.filter((item: any) => item.rule_mode == 2 && ruleDataMap[Number(item.type)])
const prompt = autoRules
.map((item: any) => {
const ruleData = ruleDataMap[Number(item.type)]
return `【${item.name || ruleData.label}】
请根据选手提交的${ruleData.label}进行评分(满分${item.percent}分)
${ruleData.extra ? `${ruleData.extra}\n` : ''}
选手提交内容:
${JSON.stringify(ruleData.data)}
评分标准:
${item.content || '请根据提交内容客观评分,并说明扣分原因。'}`
})
.join('\n\n')
const outputExample = Object.fromEntries(
autoRules.map((item: any) => [ruleDataMap[Number(item.type)].key, { score: 0, comment: '请填写评分依据' }]),
)
const fullPrompt = `${prompt}
只输出 JSON,不要输出其他内容。每个评分项都必须返回,score 不得低于 0 或高于该项满分。
JSON 输出格式:
${JSON.stringify(outputExample, null, 2)}`
const messages = [
{
role: 'system',
content: `你是一位专业的电商运营评分专家,负责对选手的商品信息维护操作进行客观、准确的评分。`,
content: `你是一位专业的大赛评分专家,负责根据评分规则对选手提交的内容进行客观、准确的评分。`,
},
{ role: 'user', content: prompt },
{ role: 'user', content: fullPrompt },
]
const result = await generateText({
model: 'deepseek-v4-flash-260425',
......@@ -169,7 +169,20 @@ ${JSON.stringify(liveData.speeches)}`
if (!result) return
try {
const scoreDetails: Record<string, ScoreItem> = JSON.parse(result.content)
const parsed = JSON.parse(result.content)
const scoreDetails: Record<string, ScoreItem> = Object.fromEntries(
autoRules.map((item: any) => {
const key = ruleDataMap[Number(item.type)].key
const score = Number(parsed[key]?.score)
return [
key,
{
score: Number.isFinite(score) ? Math.max(0, Math.min(item.percent, score)) : 0,
comment: String(parsed[key]?.comment || ''),
},
]
}),
)
const totalScore = Object.values(scoreDetails).reduce((acc, curr) => acc + (curr.score || 0), 0)
console.log(scoreDetails)
const myScore: ScoreDetail = {
......@@ -181,9 +194,10 @@ ${JSON.stringify(liveData.speeches)}`
updated_at: new Date().toLocaleString(),
}
const scores = [...detail.scores.filter((item: any) => item.checker_id != userStore.user?.id), myScore]
// 总平均分,过滤掉AI评分
const validScores = scores.filter((item) => !item.is_ai)
const totalAverageScore = validScores.reduce((acc, curr) => acc + (curr.total_score || 0), 0) / validScores.length
const scoresForTotal = includeAIScore ? scores : scores.filter((item) => !item.is_ai)
const totalAverageScore = scoresForTotal.length
? scoresForTotal.reduce((acc, curr) => acc + (curr.total_score || 0), 0) / scoresForTotal.length
: 0
await updateScore({
id: detail.id,
check_status: 2,
......
......@@ -52,10 +52,7 @@ const listOptions = computed(() => {
try {
const scores = row.score_details ? JSON.parse(row.score_details) : []
return Array.isArray(scores)
? scores
.filter((item) => !item.is_ai)
.map((item) => `${item.checker_name}(${item.total_score})`)
.join('<br/>')
? scores.map((item) => `${item.is_ai ? 'AI' : item.checker_name}(${item.total_score})`).join('<br/>')
: ''
} catch (error) {
console.log('评分人解析失败', error)
......
<script setup>
import { getScoreDetail, updateScore } from '../api'
import { ElMessage } from 'element-plus'
import { useChat } from '@ezijing/ai-vue'
import { generatePrompt } from '../prompt'
import { generatePrompt as generatePrompt2 } from '../prompt2'
import { cloneDeep } from 'lodash-es'
import { useUserStore } from '@/stores/user'
import { useEventListener } from '@vueuse/core'
......@@ -16,8 +13,6 @@ const CompetitionOperations = defineAsyncComponent(() => import('../components/C
const route = useRoute()
const id = route.query.id
const { isLoading, generateText } = useChat({ provider: 'volcano' })
const detail = ref(null)
const scoreDetails = reactive({})
......@@ -61,26 +56,6 @@ onMounted(() => {
fetchDetail()
})
const handleAIScore = async () => {
console.log('AI一键评分')
const prompt =
detail.value.competition_rule?.questions == 1
? generatePrompt(detail.value.live_data)
: generatePrompt2(detail.value.live_data)
const result = await generateText({ prompt, response_format: { type: 'json_object' } })
try {
const parsed = JSON.parse(result.content)
Object.assign(scoreDetails, parsed)
commitScore(1, true).then(() => {
ElMessage.success('保存成功')
})
} catch (error) {
console.log(error)
ElMessage.error('评分失败')
return
}
}
const handleSave = () => {
commitScore().then(() => {
ElMessage.success('保存成功')
......@@ -100,9 +75,9 @@ const commitScore = (status = 1, isAI = false) => {
// 去掉当前评分人的评分,并添加当前评分人的评分
scores = [...scores.filter((item) => !(item.checker_id == userStore.user.id && !item.is_ai)), myScore]
// 总平均分,过滤掉AI评分
const validScores = scores.filter((item) => !item.is_ai)
const totalAverageScore = validScores.reduce((acc, curr) => acc + (curr.total_score || 0), 0) / validScores.length
const totalAverageScore = scores.length
? scores.reduce((acc, curr) => acc + (curr.total_score || 0), 0) / scores.length
: 0
return updateScore({
id,
......@@ -144,14 +119,6 @@ useEventListener(window, 'pagehide', () => {
<el-form-item label="状态">{{ detail.check_status_name }}</el-form-item>
<el-form-item label="提交时间">{{ detail.commit_time }}</el-form-item>
<el-form-item>
<el-button
type="primary"
:loading="isLoading"
@click="handleAIScore"
v-if="detail.competition_rule?.competition == 1"
v-show="false"
>AI一键评分</el-button
>
<el-button type="primary" @click="handlePublishScore">提交成绩</el-button>
</el-form-item>
</el-form>
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论