120 lines
4.2 KiB
TypeScript
120 lines
4.2 KiB
TypeScript
import { NextResponse } from 'next/server'
|
||
import { llmClient } from '@/app/api/llm-client'
|
||
|
||
const SYSTEM_PROMPT = `你是一个专门分析互联网大厂老板PUA话术的AI裁判。
|
||
|
||
你的任务分两步:
|
||
1. 判断当前输入是否与历史记录重复或高度相似(意思相近算相似,不只是字面相同)
|
||
2. 对当前输入进行鸡血值评分
|
||
|
||
相似度判断标准(similarity字段,0.0-1.0):
|
||
- 0.0-0.3:全新内容,没有相似之处
|
||
- 0.3-0.6:有一定相关,但表达不同
|
||
- 0.6-0.8:明显相似,换汤不换药
|
||
- 0.8-1.0:基本重复,几乎一样的意思
|
||
|
||
评分标准(score字段,1-10分):
|
||
- 1-3分:废话/没营养 → backfire效果
|
||
- 4-6分:标准大厂黑话 → 中等效果
|
||
- 7-8分:强力PUA → 攻速暴增
|
||
- 9-10分:终极毒鸡汤 → 全场狂暴
|
||
|
||
只返回如下JSON,不要其他内容:
|
||
{
|
||
"similarity": 数字(0.0-1.0),
|
||
"similarTo": "最相似的历史内容摘要,没有则为空字符串",
|
||
"score": 数字(1-10),
|
||
"title": "效果名称(2-6个汉字,有创意)",
|
||
"desc": "对话语的一句话点评(15字以内,带点讽刺)",
|
||
"effect": "attack_boost" | "speed_boost" | "money_rain" | "rage_mode" | "backfire"
|
||
}`
|
||
|
||
// 重复惩罚倍率:根据相似度递增
|
||
function getDuplicatePenaltyMultiplier(similarity: number): number {
|
||
if (similarity >= 0.8) return 2.0 // 严重重复:再扣双倍
|
||
if (similarity >= 0.6) return 1.0 // 明显相似:再扣一倍
|
||
return 0 // 不额外惩罚
|
||
}
|
||
|
||
export async function POST(req: Request) {
|
||
try {
|
||
const { text, history } = await req.json() as {
|
||
text: string
|
||
history: string[] // 最近N条历史原文
|
||
}
|
||
|
||
if (!text || typeof text !== 'string' || text.trim().length < 2) {
|
||
return NextResponse.json({ error: '输入内容太短了' }, { status: 400 })
|
||
}
|
||
|
||
// 构建带历史的 prompt
|
||
const historyBlock = history && history.length > 0
|
||
? `\n\n历史记录(已经说过的话,按时间倒序):\n${history.slice(0, 5).map((h, i) => `${i + 1}. "${h}"`).join('\n')}`
|
||
: '\n\n历史记录:(暂无)'
|
||
|
||
const userMsg = `当前输入:"${text.slice(0, 200)}"${historyBlock}`
|
||
|
||
const resp = await llmClient.chat({
|
||
model: 'gpt-4o-mini',
|
||
messages: [
|
||
{ role: 'system', content: SYSTEM_PROMPT },
|
||
{ role: 'user', content: userMsg },
|
||
],
|
||
maxTokens: 200,
|
||
temperature: 0.6,
|
||
})
|
||
|
||
const raw = resp.choices?.[0]?.message?.content ?? ''
|
||
const match = raw.match(/\{[\s\S]*\}/)
|
||
if (!match) throw new Error('LLM 返回格式异常')
|
||
|
||
const result = JSON.parse(match[0])
|
||
const score = Math.max(1, Math.min(10, Number(result.score) || 5))
|
||
const similarity = Math.max(0, Math.min(1, Number(result.similarity) || 0))
|
||
const penaltyMultiplier = getDuplicatePenaltyMultiplier(similarity)
|
||
const isDuplicate = similarity >= 0.6
|
||
|
||
// 如果重复,效果强制降级或 backfire
|
||
let effect = result.effect || 'attack_boost'
|
||
let adjustedScore = score
|
||
if (isDuplicate && similarity >= 0.8) {
|
||
effect = 'backfire'
|
||
adjustedScore = Math.max(1, score - 3)
|
||
} else if (isDuplicate && similarity >= 0.6) {
|
||
// 效果打半折:rage_mode→speed_boost,speed_boost→attack_boost,etc.
|
||
const downgrade: Record<string, string> = {
|
||
rage_mode: 'speed_boost',
|
||
speed_boost: 'attack_boost',
|
||
money_rain: 'attack_boost',
|
||
attack_boost: 'backfire',
|
||
backfire: 'backfire',
|
||
}
|
||
effect = downgrade[effect] ?? 'backfire'
|
||
adjustedScore = Math.max(1, score - 2)
|
||
}
|
||
|
||
return NextResponse.json({
|
||
score: adjustedScore,
|
||
title: result.title || '打鸡血',
|
||
desc: result.desc || '还行吧',
|
||
effect,
|
||
similarity,
|
||
similarTo: result.similarTo || '',
|
||
isDuplicate,
|
||
penaltyMultiplier, // 前端据此额外扣 HC
|
||
})
|
||
} catch (e) {
|
||
console.error('[pua-score]', e)
|
||
return NextResponse.json({
|
||
score: 3,
|
||
title: '随机鸡血',
|
||
desc: 'AI开小差了,随机发力',
|
||
effect: 'attack_boost',
|
||
similarity: 0,
|
||
similarTo: '',
|
||
isDuplicate: false,
|
||
penaltyMultiplier: 0,
|
||
})
|
||
}
|
||
}
|