Simplify chat prompts for local LLM

This commit is contained in:
2026-04-30 18:21:41 +09:00
parent 0005352be7
commit 10fa109084
2 changed files with 37 additions and 28 deletions

View File

@@ -12,12 +12,25 @@ export interface UserUtterance {
text: string; text: string;
} }
function renderSpeakerLabel(speakerName?: string): string { export interface ChatPromptMessage {
role: "user" | "assistant";
content: string;
}
function renderSpeakerLabel(speakerName?: string): string | null {
const normalized = speakerName?.trim(); const normalized = speakerName?.trim();
if (!normalized || normalized === "unknown" || normalized === "local-user") { if (!normalized || normalized === "unknown" || normalized === "local-user") {
return "사용자"; return null;
} }
return `사용자(${normalized})`; return normalized;
}
function renderUserMessage(speakerName: string | undefined, text: string): string {
const label = renderSpeakerLabel(speakerName);
if (!label) {
return text;
}
return `${label}: ${text}`;
} }
export class ConversationMemory { export class ConversationMemory {
@@ -53,28 +66,27 @@ export class ConversationMemory {
return [...this.turns]; return [...this.turns];
} }
buildPrompt(currentUtterance: UserUtterance): string { buildMessages(currentUtterance: UserUtterance): ChatPromptMessage[] {
const recent = this.turns return [
...this.turns
.slice(-this.maxTurns) .slice(-this.maxTurns)
.map((turn) => { .map((turn) => {
if (turn.role === "assistant") { if (turn.role === "assistant") {
return `비서: ${turn.text}`; return {
role: "assistant" as const,
content: turn.text,
};
} }
return `${renderSpeakerLabel(turn.speakerName)}: ${turn.text}`; return {
}) role: "user" as const,
.join("\n\n"); content: renderUserMessage(turn.speakerName, turn.text),
};
const historyBlock = recent.length > 0 ? recent : "(이전 대화 없음)"; }),
{
return [ role: "user",
"최근 대화:", content: renderUserMessage(currentUtterance.speakerName, currentUtterance.text),
historyBlock, },
"", ];
"지금 방금 들은 말:",
`${renderSpeakerLabel(currentUtterance.speakerName)}: ${currentUtterance.text}`,
"",
"위 마지막 말에 자연스럽게 바로 답변해라. speaker_id나 speaker_name 같은 메타 정보를 자기소개처럼 반복하지 마라.",
].join("\n");
} }
private trim(): void { private trim(): void {

View File

@@ -7,9 +7,9 @@ const ASSISTANT_INSTRUCTIONS = [
"답변은 짧고 실용적으로 한다.", "답변은 짧고 실용적으로 한다.",
"기본은 한 문장, 길어도 두 문장을 넘기지 않는다.", "기본은 한 문장, 길어도 두 문장을 넘기지 않는다.",
"말투는 자연스러운 한국어로 유지한다.", "말투는 자연스러운 한국어로 유지한다.",
"speaker_id와 speaker_name은 화자 구분용이므로 필요할 때만 자연스럽게 반영한다.", "대화 기록에 이름이 붙어 있을 수 있지만, 이름이나 메타 정보를 그대로 따라 말하지 않는다.",
"자기소개를 반복하지 말고, 사용자가 정체를 물었을 때만 짧게 소개한다.", "자기소개를 반복하지 말고, 정체를 물으면 '저는 로컬 음성 비서입니다.' 정도로만 짧게 한다.",
"\"저는 화자입니다\" 같은 어색한 메타 응답은 하지 않는다.", "\"저는 화자입니다\" 같은 어색한 메타 응답은 절대 하지 않는다.",
"잘 못 들었거나 의미가 불명확하면 짧게 다시 물어본다.", "잘 못 들었거나 의미가 불명확하면 짧게 다시 물어본다.",
"목록, 마크다운, 코드블록은 쓰지 않는다.", "목록, 마크다운, 코드블록은 쓰지 않는다.",
"생각 과정을 드러내지 말고 최종 답변만 말한다.", "생각 과정을 드러내지 말고 최종 답변만 말한다.",
@@ -95,10 +95,7 @@ export class OllamaLlmService implements LlmService {
role: "system", role: "system",
content: ASSISTANT_INSTRUCTIONS, content: ASSISTANT_INSTRUCTIONS,
}, },
{ ...memory.buildMessages(utterance),
role: "user",
content: memory.buildPrompt(utterance),
},
], ],
think: false, think: false,
stream: false, stream: false,