feat(tag-vocab): prompt.ts — PROMPT_VERSION 4 + vocab parameter (#3 v0.2.3)

- PROMPT_VERSION 3 → 4 (marker bump, retry 트리거 X)
- buildPrompt 4번째 param vocab: string[] = []
- vocab.length > 0 시 "Existing vocabulary tags" + "Prefer reusing" 라인 추가
- vocab=[] 시 라인 자체 생략 (Q3=B 결정)
- 단위 +4 cases (신규 prompt.test.ts)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
altair823
2026-05-02 12:17:17 +09:00
parent e2b16d44d7
commit 134d59ddb4
2 changed files with 39 additions and 3 deletions

View File

@@ -1,21 +1,26 @@
import type { ParseResult } from '../services/dueDateParser.js';
export const PROMPT_VERSION = 3;
export const PROMPT_VERSION = 4;
export function buildPrompt(
rawText: string,
todayKst: string,
candidates: ParseResult[] = []
candidates: ParseResult[] = [],
vocab: string[] = []
): string {
const candidateBlock = candidates.length > 0
? `\nDate candidates extracted by a Korean rule parser (these are HINTS — you decide which is correct, or pick null):
${candidates.map((c, i) => ` ${i + 1}. ${c.iso ?? '(ambiguous)'} — matched token: "${c.matchedToken ?? '?'}" (confidence: ${c.confidence ?? 'low'})`).join('\n')}\n`
: '';
const vocabBlock = vocab.length > 0
? `\nExisting vocabulary tags (most-used first): ${vocab.join(', ')}\nPrefer reusing a vocabulary tag when the meaning matches; create new tags only when the meaning is genuinely new.\n`
: '';
return `You organize raw personal notes into structured metadata.
Today's date in Korea Standard Time (KST): ${todayKst}
${candidateBlock}
${candidateBlock}${vocabBlock}
Input note (raw text, may be fragmented, any language):
---
${rawText}

31
tests/unit/prompt.test.ts Normal file
View File

@@ -0,0 +1,31 @@
import { describe, it, expect } from 'vitest';
import { buildPrompt, PROMPT_VERSION } from '@main/ai/prompt.js';
describe('prompt', () => {
it('PROMPT_VERSION is 4', () => {
expect(PROMPT_VERSION).toBe(4);
});
it('buildPrompt with empty vocab omits vocabulary line entirely', () => {
const out = buildPrompt('hello', '2026-05-02', [], []);
expect(out).not.toContain('vocabulary');
expect(out).not.toContain('Prefer reusing');
});
it('buildPrompt with vocab includes Prefer instruction + comma-separated list', () => {
const out = buildPrompt('hello', '2026-05-02', [], ['design', 'meeting', 'qa']);
expect(out).toContain('Existing vocabulary tags');
expect(out).toContain('design, meeting, qa');
expect(out).toContain('Prefer reusing');
});
it('vocab block appears between candidate block and JSON rules', () => {
const out = buildPrompt('hello', '2026-05-02', [], ['design']);
const candidateIdx = out.indexOf("Today's date");
const vocabIdx = out.indexOf('Existing vocabulary');
const jsonRulesIdx = out.indexOf('Return a JSON object');
expect(candidateIdx).toBeGreaterThan(-1);
expect(vocabIdx).toBeGreaterThan(candidateIdx);
expect(jsonRulesIdx).toBeGreaterThan(vocabIdx);
});
});