diff --git a/src/services/conversation.ts b/src/services/conversation.ts index de209af..1b4bb90 100644 --- a/src/services/conversation.ts +++ b/src/services/conversation.ts @@ -12,12 +12,25 @@ export interface UserUtterance { 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(); 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 { @@ -53,28 +66,27 @@ export class ConversationMemory { return [...this.turns]; } - buildPrompt(currentUtterance: UserUtterance): string { - const recent = this.turns + buildMessages(currentUtterance: UserUtterance): ChatPromptMessage[] { + return [ + ...this.turns .slice(-this.maxTurns) .map((turn) => { if (turn.role === "assistant") { - return `비서: ${turn.text}`; + return { + role: "assistant" as const, + content: turn.text, + }; } - return `${renderSpeakerLabel(turn.speakerName)}: ${turn.text}`; - }) - .join("\n\n"); - - const historyBlock = recent.length > 0 ? recent : "(이전 대화 없음)"; - - return [ - "최근 대화:", - historyBlock, - "", - "지금 방금 들은 말:", - `${renderSpeakerLabel(currentUtterance.speakerName)}: ${currentUtterance.text}`, - "", - "위 마지막 말에 자연스럽게 바로 답변해라. speaker_id나 speaker_name 같은 메타 정보를 자기소개처럼 반복하지 마라.", - ].join("\n"); + return { + role: "user" as const, + content: renderUserMessage(turn.speakerName, turn.text), + }; + }), + { + role: "user", + content: renderUserMessage(currentUtterance.speakerName, currentUtterance.text), + }, + ]; } private trim(): void { diff --git a/src/services/ollama-llm.ts b/src/services/ollama-llm.ts index 519a5c0..e650855 100644 --- a/src/services/ollama-llm.ts +++ b/src/services/ollama-llm.ts @@ -7,9 +7,9 @@ const ASSISTANT_INSTRUCTIONS = [ "답변은 짧고 실용적으로 한다.", "기본은 한 문장, 길어도 두 문장을 넘기지 않는다.", "말투는 자연스러운 한국어로 유지한다.", - "speaker_id와 speaker_name은 화자 구분용이므로 필요할 때만 자연스럽게 반영한다.", - "자기소개를 반복하지 말고, 사용자가 정체를 물었을 때만 짧게 소개한다.", - "\"저는 화자입니다\" 같은 어색한 메타 응답은 하지 않는다.", + "대화 기록에 이름이 붙어 있을 수 있지만, 이름이나 메타 정보를 그대로 따라 말하지 않는다.", + "자기소개를 반복하지 말고, 정체를 물으면 '저는 로컬 음성 비서입니다.' 정도로만 짧게 답한다.", + "\"저는 화자입니다\" 같은 어색한 메타 응답은 절대 하지 않는다.", "잘 못 들었거나 의미가 불명확하면 짧게 다시 물어본다.", "목록, 마크다운, 코드블록은 쓰지 않는다.", "생각 과정을 드러내지 말고 최종 답변만 말한다.", @@ -95,10 +95,7 @@ export class OllamaLlmService implements LlmService { role: "system", content: ASSISTANT_INSTRUCTIONS, }, - { - role: "user", - content: memory.buildPrompt(utterance), - }, + ...memory.buildMessages(utterance), ], think: false, stream: false,