fix: 标准答案图先发+提示词优化+JSON强化+重试3次

This commit is contained in:
2026-07-16 10:41:44 +08:00
parent ba7aff17d7
commit e7a9cd23ad
+66 -48
View File
@@ -354,7 +354,7 @@ async function callAiApi(imageBase64: string, rubric: string, config: { apiKey:
deductions: string[]
comment: string
}> {
const systemRole = `你是高校阅卷专家,严格按照评分标准逐项评分。只输出JSON,不包含任何其他文字`
const systemRole = `你是高校阅卷专家,严格按照评分标准逐项评分。你必须只输出JSON严禁包含\`\`\`markdown代码块、\`\`\`json标记、注释、说明文字或任何其他非JSON内容。输出必须以{开头,以}结尾,且必须是合法的JSON`
const hasRefImage = !!extras?.referenceAnswerImage
@@ -368,10 +368,10 @@ ${extras?.referenceAnswer || '无'}
【满分】
${extras?.maxScore ?? 100}
图片1学生答案
图片2标准答案
图片1标准答案
图片2学生答案
按照标准答案(图片2)给学生答案(图片1)评分。
根据评分标准和标准答案(图片1)对学生答案(图片2)评分。
---
@@ -382,13 +382,14 @@ ${extras?.maxScore ?? 100}
4. 部分正确时按评分标准酌情给分
【输出要求】
只输出以下JSON,不要包含markdown代码块、\`\`\`标记、注释或任何其他文字
你必须严格输出以下格式的纯JSON,严禁包含\`\`\`\`\`\`json、markdown代码块、注释或任何说明文字。整个回复必须是合法的JSON对象,以{开始,以}结束
{
"score": <总分>,
"confidence": <置信度0~1>,
"deductions": ["扣分项说明1", "扣分项说明2"],
"comment": "总评语及逐项得分说明"
}`
}
再次强调:只输出JSON,禁止任何其他内容。`
: `${extras?.questionTitle ? `【题目】\n${extras.questionTitle}\n` : ''}【评分标准】
${rubric || '无'}
@@ -398,8 +399,8 @@ ${extras?.referenceAnswer || '无'}
【满分】
${extras?.maxScore ?? 100}
学生答案
看图片中的内容
图片:学生答案
根据评分标准对学生答案评分
---
@@ -410,65 +411,82 @@ ${extras?.maxScore ?? 100}
4. 部分正确时按评分标准酌情给分
【输出要求】
只输出以下JSON,不要包含markdown代码块、\`\`\`标记、注释或任何其他文字
你必须严格输出以下格式的纯JSON,严禁包含\`\`\`\`\`\`json、markdown代码块、注释或任何说明文字。整个回复必须是合法的JSON对象,以{开始,以}结束
{
"score": <总分>,
"confidence": <置信度0~1>,
"deductions": ["扣分项说明1", "扣分项说明2"],
"comment": "总评语及逐项得分说明"
}`
}
再次强调:只输出JSON,禁止任何其他内容。`
const axios = require('axios')
const userContent: any[] = hasRefImage
? [
{ type: 'text', text: prompt },
{ type: 'image_url', image_url: { url: `data:image/png;base64,${imageBase64}` } },
{ type: 'image_url', image_url: { url: extras!.referenceAnswerImage } }
{ type: 'image_url', image_url: { url: extras!.referenceAnswerImage } },
{ type: 'image_url', image_url: { url: `data:image/png;base64,${imageBase64}` } }
]
: [
{ type: 'text', text: prompt },
{ type: 'image_url', image_url: { url: `data:image/png;base64,${imageBase64}` } }
]
const response = await axios.post(`${config.baseUrl}/chat/completions`, {
model: config.model,
messages: [
{
role: 'system',
content: systemRole
},
{
role: 'user',
content: userContent
}
],
max_tokens: 4096,
temperature: 0.3
}, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${config.apiKey}`
},
timeout: ((getStore().get('timeout') as number) ?? 120) * 1000
})
let lastError: Error | null = null
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const response = await axios.post(`${config.baseUrl}/chat/completions`, {
model: config.model,
messages: [
{
role: 'system',
content: systemRole
},
{
role: 'user',
content: userContent
}
],
max_tokens: 4096,
temperature: 0.3
}, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${config.apiKey}`
},
timeout: ((getStore().get('timeout') as number) ?? 120) * 1000
})
const content = response.data?.choices?.[0]?.message?.content
if (!content) {
log.error('AI API 返回为空或格式异常:', JSON.stringify(response.data).slice(0, 1000))
throw new Error(`API 返回为空,请检查模型名是否正确 (当前: ${config.model})`)
}
const parsed = tryParseJson(content)
if (!parsed) {
log.error('AI 返回 JSON 解析失败:', content.slice(0, 500))
throw new Error(`AI 返回格式异常,无法解析为 JSON。原始返回:\n${content}`)
}
return {
score: parsed.score,
confidence: parsed.confidence ?? 0.5,
deductions: parsed.deductions ?? [],
comment: parsed.comment ?? parsed.commit ?? ''
const content = response.data?.choices?.[0]?.message?.content
if (!content) {
lastError = new Error(`API 返回为空 (尝试 ${attempt}/3)`)
log.warn(`AI API empty response, attempt ${attempt}/3`)
if (attempt < 3) continue
throw lastError
}
const parsed = tryParseJson(content)
if (!parsed) {
lastError = new Error(`AI 返回 JSON 解析失败 (尝试 ${attempt}/3)`)
log.warn(`AI JSON parse failed, attempt ${attempt}/3:`, content.slice(0, 200))
if (attempt < 3) continue
throw lastError
}
return {
score: parsed.score,
confidence: parsed.confidence ?? 0.5,
deductions: parsed.deductions ?? [],
comment: parsed.comment ?? parsed.commit ?? ''
}
} catch (err: any) {
lastError = err
if (attempt < 3) {
log.info(`AI API attempt ${attempt}/3 failed, retrying...`)
await new Promise(r => setTimeout(r, 2000))
}
}
}
throw lastError || new Error('AI API 调用失败')
}
function tryParseJson(text: string): any {