optimize chat prompts

This commit is contained in:
Julian Freeman
2026-04-05 17:40:50 -04:00
parent db848bed92
commit 2e7789df63
3 changed files with 61 additions and 54 deletions

View File

@@ -181,7 +181,10 @@ const translateMessage = async (sender: 'me' | 'partner', retranslateId?: string
// 3. Prepare Prompt // 3. Prepare Prompt
const fromLang = sender === 'me' ? activeSession.value.me.language : activeSession.value.partner.language; const fromLang = sender === 'me' ? activeSession.value.me.language : activeSession.value.partner.language;
const toLang = sender === 'me' ? activeSession.value.partner.language : activeSession.value.me.language; const toLang = sender === 'me' ? activeSession.value.partner.language : activeSession.value.me.language;
const myToneLabel = TONE_REGISTER_OPTIONS.find(o => o.value === activeSession.value!.me.tone)?.label || '随和'; const targetTone = sender === 'me'
? TONE_REGISTER_OPTIONS.find(o => o.value === activeSession.value!.me.tone)?.value || 'Polite & Conversational'
: 'Auto-detect';
const senderName = sender === 'me' ? activeSession.value.me.name : activeSession.value.partner.name;
const systemPrompt = settings.chatSystemPromptTemplate const systemPrompt = settings.chatSystemPromptTemplate
.replace(/{ME_NAME}/g, activeSession.value.me.name) .replace(/{ME_NAME}/g, activeSession.value.me.name)
@@ -191,15 +194,16 @@ const translateMessage = async (sender: 'me' | 'partner', retranslateId?: string
.replace(/{PART_GENDER}/g, activeSession.value.partner.gender) .replace(/{PART_GENDER}/g, activeSession.value.partner.gender)
.replace(/{PART_LANG}/g, activeSession.value.partner.language.englishName) .replace(/{PART_LANG}/g, activeSession.value.partner.language.englishName)
.replace(/{HISTORY_BLOCK}/g, historyBlock || 'None (This is the start of conversation)') .replace(/{HISTORY_BLOCK}/g, historyBlock || 'None (This is the start of conversation)')
.replace(/{SENDER_NAME}/g, senderName)
.replace(/{FROM_LANG}/g, fromLang.englishName) .replace(/{FROM_LANG}/g, fromLang.englishName)
.replace(/{TO_LANG}/g, toLang.englishName) .replace(/{TO_LANG}/g, toLang.englishName)
.replace(/{MY_TONE}/g, myToneLabel); .replace(/{TARGET_TONE}/g, targetTone);
const requestBody = { const requestBody = {
model: settings.modelName, model: settings.modelName,
messages: [ messages: [
{ role: "system", content: systemPrompt }, { role: "system", content: systemPrompt },
{ role: "user", content: text } { role: "user", content: `[Text to Translate]\n${text}` }
], ],
stream: settings.enableStreaming stream: settings.enableStreaming
}; };
@@ -271,8 +275,8 @@ const evaluateMessage = async (messageId: string, force = false) => {
// 动态确定目标语气约束 // 动态确定目标语气约束
const targetTone = msg.sender === 'me' const targetTone = msg.sender === 'me'
? (TONE_REGISTER_OPTIONS.find(o => o.value === activeSession.value!.me.tone)?.label || '随和') ? (TONE_REGISTER_OPTIONS.find(o => o.value === activeSession.value!.me.tone)?.value || 'Polite & Conversational')
: '自动识别 (保留原作者原始语气和情绪)'; : 'Auto-detect';
const systemPrompt = settings.chatEvaluationPromptTemplate const systemPrompt = settings.chatEvaluationPromptTemplate
.replace(/{ME_NAME}/g, activeSession.value.me.name) .replace(/{ME_NAME}/g, activeSession.value.me.name)
@@ -285,9 +289,9 @@ const evaluateMessage = async (messageId: string, force = false) => {
.replace(/{TARGET_TONE}/g, targetTone) .replace(/{TARGET_TONE}/g, targetTone)
.replace(/{SENDER_NAME}/g, senderName) .replace(/{SENDER_NAME}/g, senderName)
.replace(/{FROM_LANG}/g, fromLang.englishName) .replace(/{FROM_LANG}/g, fromLang.englishName)
.replace(/{TO_LANG}/g, toLang.englishName) .replace(/{TO_LANG}/g, toLang.englishName);
.replace(/{ORIGINAL_TEXT}/g, msg.original) // .replace(/{ORIGINAL_TEXT}/g, msg.original)
.replace(/{CURRENT_TRANSLATION}/g, msg.translated); // .replace(/{CURRENT_TRANSLATION}/g, msg.translated);
const userPrompt = `[Source Text]\n${msg.original}\n\n[Current Translation]\n${msg.translated}`; const userPrompt = `[Source Text]\n${msg.original}\n\n[Current Translation]\n${msg.translated}`;
@@ -367,14 +371,15 @@ const refineMessage = async (messageId: string) => {
return `${senderName}: "${m.original}"`; return `${senderName}: "${m.original}"`;
}).join('\n'); }).join('\n');
const myToneLabel = TONE_REGISTER_OPTIONS.find(o => o.value === activeSession.value!.me.tone)?.label || '随和';
// 确定目标语气 // 确定目标语气
const targetTone = msg.sender === 'me' ? myToneLabel : '自动识别 (保持原作者原始语气和情绪)'; const targetTone = msg.sender === 'me'
? TONE_REGISTER_OPTIONS.find(o => o.value === activeSession.value!.me.tone)?.value || 'Polite & Conversational'
: 'Auto-detect';
// 动态确定语言方向 // 动态确定语言方向
const fromLang = msg.sender === 'me' ? activeSession.value.me.language : activeSession.value.partner.language; const fromLang = msg.sender === 'me' ? activeSession.value.me.language : activeSession.value.partner.language;
const toLang = msg.sender === 'me' ? activeSession.value.partner.language : activeSession.value.me.language; const toLang = msg.sender === 'me' ? activeSession.value.partner.language : activeSession.value.me.language;
const senderName = msg.sender === 'me' ? activeSession.value.me.name : activeSession.value.partner.name;
const systemPrompt = settings.chatRefinementPromptTemplate const systemPrompt = settings.chatRefinementPromptTemplate
.replace(/{ME_NAME}/g, activeSession.value.me.name) .replace(/{ME_NAME}/g, activeSession.value.me.name)
@@ -384,10 +389,11 @@ const refineMessage = async (messageId: string) => {
.replace(/{PART_GENDER}/g, activeSession.value.partner.gender) .replace(/{PART_GENDER}/g, activeSession.value.partner.gender)
.replace(/{PART_LANG}/g, activeSession.value.partner.language.englishName) .replace(/{PART_LANG}/g, activeSession.value.partner.language.englishName)
.replace(/{HISTORY_BLOCK}/g, historyBlock || 'None') .replace(/{HISTORY_BLOCK}/g, historyBlock || 'None')
.replace(/{ORIGINAL_TEXT}/g, msg.original) // .replace(/{ORIGINAL_TEXT}/g, msg.original)
.replace(/{CURRENT_TRANSLATION}/g, msg.translated) // .replace(/{CURRENT_TRANSLATION}/g, msg.translated)
.replace(/{SUGGESTIONS}/g, suggestionsText) // .replace(/{SUGGESTIONS}/g, suggestionsText)
.replace(/{TARGET_TONE}/g, targetTone) .replace(/{TARGET_TONE}/g, targetTone)
.replace(/{SENDER_NAME}/g, senderName)
.replace(/{FROM_LANG}/g, fromLang.englishName) .replace(/{FROM_LANG}/g, fromLang.englishName)
.replace(/{TO_LANG}/g, toLang.englishName); .replace(/{TO_LANG}/g, toLang.englishName);
@@ -409,7 +415,7 @@ const refineMessage = async (messageId: string) => {
model: refineModelName, model: refineModelName,
messages: [ messages: [
{ role: "system", content: systemPrompt }, { role: "system", content: systemPrompt },
{ role: "user", content: `Please refine the following original text: ${msg.original}` } { role: "user", content: `[Source Text]\n${msg.original}\n\n[Current Translation]\n${msg.translated}\n\n[Suggestions]\n${suggestionsText}` }
], ],
stream: settings.enableStreaming stream: settings.enableStreaming
}; };

View File

@@ -536,7 +536,7 @@ onUnmounted(() => window.removeEventListener('click', handleGlobalClick));
></textarea> ></textarea>
<div class="px-5 py-3 bg-slate-50 dark:bg-slate-950 border-t dark:border-slate-800"> <div class="px-5 py-3 bg-slate-50 dark:bg-slate-950 border-t dark:border-slate-800">
<div class="flex flex-wrap gap-1.5"> <div class="flex flex-wrap gap-1.5">
<span v-for="tag in ['{ME_NAME}', '{ME_GENDER}', '{ME_LANG}', '{PART_NAME}', '{PART_GENDER}', '{PART_LANG}', '{HISTORY_BLOCK}', '{FROM_LANG}', '{TO_LANG}', '{MY_TONE}']" :key="tag" class="px-2 py-0.5 bg-white dark:bg-slate-800 text-[10px] font-mono rounded-md border dark:border-slate-700 text-slate-500 shadow-sm">{{ tag }}</span> <span v-for="tag in ['{ME_NAME}', '{ME_GENDER}', '{ME_LANG}', '{PART_NAME}', '{PART_GENDER}', '{PART_LANG}', '{HISTORY_BLOCK}', '{SENDER_NAME}', '{FROM_LANG}', '{TO_LANG}', '{TARGET_TONE}']" :key="tag" class="px-2 py-0.5 bg-white dark:bg-slate-800 text-[10px] font-mono rounded-md border dark:border-slate-700 text-slate-500 shadow-sm">{{ tag }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -558,7 +558,7 @@ onUnmounted(() => window.removeEventListener('click', handleGlobalClick));
></textarea> ></textarea>
<div class="px-5 py-3 bg-slate-50 dark:bg-slate-950 border-t dark:border-slate-800"> <div class="px-5 py-3 bg-slate-50 dark:bg-slate-950 border-t dark:border-slate-800">
<div class="flex flex-wrap gap-1.5"> <div class="flex flex-wrap gap-1.5">
<span v-for="tag in ['{ME_NAME}', '{ME_GENDER}', '{ME_LANG}', '{PART_NAME}', '{PART_GENDER}', '{PART_LANG}', '{TARGET_TONE}', '{SENDER_NAME}', '{FROM_LANG}', '{TO_LANG}', '{HISTORY_BLOCK}', '{ORIGINAL_TEXT}', '{CURRENT_TRANSLATION}']" :key="tag" class="px-2 py-0.5 bg-white dark:bg-slate-800 text-[10px] font-mono rounded-md border dark:border-slate-700 text-slate-500 shadow-sm">{{ tag }}</span> <span v-for="tag in ['{ME_NAME}', '{ME_GENDER}', '{ME_LANG}', '{PART_NAME}', '{PART_GENDER}', '{PART_LANG}', '{HISTORY_BLOCK}', '{SENDER_NAME}', '{FROM_LANG}', '{TO_LANG}', '{TARGET_TONE}']" :key="tag" class="px-2 py-0.5 bg-white dark:bg-slate-800 text-[10px] font-mono rounded-md border dark:border-slate-700 text-slate-500 shadow-sm">{{ tag }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -580,7 +580,7 @@ onUnmounted(() => window.removeEventListener('click', handleGlobalClick));
></textarea> ></textarea>
<div class="px-5 py-3 bg-slate-50 dark:bg-slate-950 border-t dark:border-slate-800"> <div class="px-5 py-3 bg-slate-50 dark:bg-slate-950 border-t dark:border-slate-800">
<div class="flex flex-wrap gap-1.5"> <div class="flex flex-wrap gap-1.5">
<span v-for="tag in ['{ME_NAME}', '{ME_GENDER}', '{ME_LANG}', '{PART_NAME}', '{PART_GENDER}', '{PART_LANG}', '{TARGET_TONE}', '{HISTORY_BLOCK}', '{ORIGINAL_TEXT}', '{CURRENT_TRANSLATION}', '{SUGGESTIONS}']" :key="tag" class="px-2 py-0.5 bg-white dark:bg-slate-800 text-[10px] font-mono rounded-md border dark:border-slate-700 text-slate-500 shadow-sm">{{ tag }}</span> <span v-for="tag in ['{ME_NAME}', '{ME_GENDER}', '{ME_LANG}', '{PART_NAME}', '{PART_GENDER}', '{PART_LANG}', '{HISTORY_BLOCK}', '{SENDER_NAME}', '{FROM_LANG}', '{TO_LANG}', '{TARGET_TONE}']" :key="tag" class="px-2 py-0.5 bg-white dark:bg-slate-800 text-[10px] font-mono rounded-md border dark:border-slate-700 text-slate-500 shadow-sm">{{ tag }}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -154,50 +154,54 @@ export interface ChatSession {
lastActivity: string; lastActivity: string;
} }
export const CONVERSATION_SYSTEM_PROMPT_TEMPLATE = `You are a professional real-time conversation translator. export const CONVERSATION_SYSTEM_PROMPT_TEMPLATE = `# Role: Professional Real-time Conversation Translator
Current Context:
- Role A (Me): {ME_NAME}, Gender: {ME_GENDER}, Language: {ME_LANG}.
- Role B (Other): {PART_NAME}, Gender: {PART_GENDER}, Language: {PART_LANG}.
[Conversation History] # Participants:
- Participant A: [Name: {ME_NAME}, Gender: {ME_GENDER}, Language: {ME_LANG}]
- Participant B: [Name: {PART_NAME}, Gender: {PART_GENDER}, Language: {PART_LANG}]
# Recent Conversation Flow:
{HISTORY_BLOCK} {HISTORY_BLOCK}
[Current Task] # Current Turn to Translate:
Translate the incoming text from {FROM_LANG} to {TO_LANG}. - Speaker: {SENDER_NAME}
- Source Language: {FROM_LANG}
- Target Language: {TO_LANG}
- Intended Tone/Register: {TARGET_TONE}
[Constraints] # Constraints
1. Contextual Awareness: Use the [Conversation History] to resolve pronouns (it, that, etc.) and maintain consistency. 1. Contextual Awareness: Use the [Conversation History] to resolve pronouns (it, that, etc.) and maintain consistency.
2. Tone & Register: 2. Personalization & Tone: Ensure all grammatical agreements and self-referential terms reflect speaker's gender. Faithfully replicate the intended tone, maintaining the formality, emotional nuance, and unique style of the source text without neutralizing it.
- If translating for 'Me', strictly use the tone: {MY_TONE}.
- If translating for 'Other', auto-detect and preserve their original tone/emotion.
3. Natural Flow: Keep the translation concise and natural for a chat environment. Avoid "translationese". 3. Natural Flow: Keep the translation concise and natural for a chat environment. Avoid "translationese".
4. Strictly avoid over-translation: Do not add extra information not present in the source text. 4. Strictly avoid over-translation: Do not add extra information not present in the source text.
5. Output ONLY the translated text, no explanations.`; 5. Output ONLY the translated text, no explanations.`;
export const CONVERSATION_EVALUATION_PROMPT_TEMPLATE = `# Role: Expert Conversation Auditor export const CONVERSATION_EVALUATION_PROMPT_TEMPLATE = `# Role: Expert Conversation Auditor
# Context Info # Participants:
- Role A (Me): {ME_NAME} ({ME_GENDER}, Native {ME_LANG}) - Participant A: [Name: {ME_NAME}, Gender: {ME_GENDER}, Language: {ME_LANG}]
- Role B (Other): {PART_NAME} ({PART_GENDER}, Native {PART_LANG}) - Participant B: [Name: {PART_NAME}, Gender: {PART_GENDER}, Language: {PART_LANG}]
# Recent Conversation Flow (Original Messages Only): # Recent Conversation Flow:
{HISTORY_BLOCK} {HISTORY_BLOCK}
# Current Turn to Audit: # Current Turn to Audit:
- Speaker: {SENDER_NAME} - Speaker: {SENDER_NAME}
- Direction: {FROM_LANG} -> {TO_LANG} - Source Language: {FROM_LANG}
- Source Text ({FROM_LANG}): {ORIGINAL_TEXT} - Target Language: {TO_LANG}
- Translation ({TO_LANG}): {CURRENT_TRANSLATION} - Intended Tone/Register: {TARGET_TONE}
# Audit Priorities # Audit Criteria
1. **Contextual Accuracy**: Does the translation correctly reflect the meaning based on the [Recent Conversation Flow]? 1. **Contextual Inaccuracy**: Failure to correctly reflect the meaning based on the [Recent Conversation Flow]
2. **Relational Tone**: 2. **Semantic Error**: Objective misalignment with the source meaning or complete hallucinations.
- If the Speaker is 'Me', does it match the target tone({TARGET_TONE})? 3. **Grammatical Error**: Clear violations of target language grammar or syntax rules.
- If the Speaker is 'Other', does it preserve their original tone/emotion? 4. **Tone Failure**: A tone that is the opposite or significantly different from the [Intended Tone/Register].
3. **Naturalness**: Is it concise and fit for an IM (Instant Messaging) environment? 5. **Poor Readability**: The phrasing is so stiff, unnatural, or convoluted that it hinders smooth comprehension (e.g., obvious "translationese" or broken logic).
**Note**: Do NOT penalize if the translation is simply "not the most elegant" or if there's a subjective preference for a different synonym. If it's natural enough for a native speaker to understand without effort, it's acceptable.
# Instructions # Instructions
1. **Evaluation**: Compare the [Source Text] and [Translation] based on the [Audit Priorities]. 1. **Evaluation**: Compare the [Source Text] and [Translation] based on the [Audit Criteria].
2. **Scoring Strategy**: 2. **Scoring Strategy**:
- **90-100**: Accurate, grammatically sound, and flows naturally. - **90-100**: Accurate, grammatically sound, and flows naturally.
- **75-89**: Accurate meaning, but suffers from "stiff" phrasing or minor flow issues that need adjustment. - **75-89**: Accurate meaning, but suffers from "stiff" phrasing or minor flow issues that need adjustment.
@@ -217,26 +221,23 @@ Respond ONLY in JSON. "analysis" and "text" MUST be in Simplified Chinese:
export const CONVERSATION_REFINEMENT_PROMPT_TEMPLATE = `# Role: Professional Conversation Editor export const CONVERSATION_REFINEMENT_PROMPT_TEMPLATE = `# Role: Professional Conversation Editor
# Context: # Participants:
- Role A (Me): {ME_NAME} ({ME_GENDER}, Native {ME_LANG}) - Participant A: [Name: {ME_NAME}, Gender: {ME_GENDER}, Language: {ME_LANG}]
- Role B (Other): {PART_NAME} ({PART_GENDER}, Native {PART_LANG}) - Participant B: [Name: {PART_NAME}, Gender: {PART_GENDER}, Language: {PART_LANG}]
# Recent Conversation Flow (Original Messages Only): # Recent Conversation Flow:
{HISTORY_BLOCK} {HISTORY_BLOCK}
# Current Turn to Refine: # Current Turn to Refine:
- Speaker: {SENDER_NAME} - Speaker: {SENDER_NAME}
- Direction: {FROM_LANG} -> {TO_LANG} - Source Language: {FROM_LANG}
- Source Text ({FROM_LANG}): {ORIGINAL_TEXT} - Target Language: {TO_LANG}
- Current Translation ({TO_LANG}): {CURRENT_TRANSLATION} - Intended Tone/Register: {TARGET_TONE}
# Suggestions to Apply:
{SUGGESTIONS}
# Instructions: # Instructions:
1. Carefully review the [Suggestions] and apply the requested improvements to the [Current Translation] while ensuring it fits naturally into the [Recent Conversation Flow]. 1. Carefully review the [Suggestions] and apply the requested improvements to the [Current Translation] while ensuring it fits naturally into the [Recent Conversation Flow].
2. Ensure that the refined translation remains semantically identical to the [Source Text]. 2. Ensure that the refined translation remains semantically identical to the [Source Text].
3. If the Speaker is 'Me', strictly maintain the target tone({TARGET_TONE}); if the Speaker is 'Other', auto-detect and preserve their original tone/emotion. 3. Maintain the speaker's gender and [Intended Tone/Register] as specified.
4. If a piece of feedback contradicts the [Source Text], prioritize accuracy and provide a balanced refinement. 4. If a piece of feedback contradicts the [Source Text], prioritize accuracy and provide a balanced refinement.
5. Produce ONLY the refined {TO_LANG} translation, without any additional explanations, notes, or commentary.`; 5. Produce ONLY the refined {TO_LANG} translation, without any additional explanations, notes, or commentary.`;